killbill-aplcache
Changes
.idea/compiler.xml 68(+34 -34)
.idea/modules.xml 3(+1 -2)
account/src/main/java/org/killbill/billing/account/api/svcs/DefaultAccountInternalApi.java 38(+10 -28)
account/src/main/java/org/killbill/billing/account/api/svcs/DefaultImmutableAccountInternalApi.java 103(+103 -0)
account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountChangeEvent.java 57(+33 -24)
account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApi.java 10(+5 -5)
account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java 6(+3 -3)
api/src/main/java/org/killbill/billing/subscription/api/migration/SubscriptionBaseMigrationApi.java 133(+0 -133)
api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseTransitionType.java 12(+0 -12)
api/src/main/java/org/killbill/billing/subscription/api/timeline/SubscriptionBaseTimeline.java 10(+0 -10)
beatrix/pom.xml 6(+6 -0)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/BeatrixIntegrationModuleNoDB.java 32(+26 -6)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestBillingAlignment.java 8(+4 -4)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueBase.java 9(+6 -3)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java 52(+37 -15)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java 25(+14 -11)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithTags.java 16(+10 -6)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java 49(+19 -30)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoice.java 81(+36 -45)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java 95(+45 -50)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java 26(+13 -13)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java 30(+15 -15)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithDifferentBillingPeriods.java 66(+33 -33)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithWrittenOffTag.java 6(+3 -3)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoiceNotifications.java 9(+4 -5)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java 379(+351 -28)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestMigrationSubscriptions.java 249(+249 -0)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithEntilementPlugin.java 27(+15 -12)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithPriceOverride.java 28(+12 -16)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java 50(+25 -25)
catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java 19(+10 -9)
entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java 156(+130 -26)
entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultBlockingTransitionInternalEvent.java 40(+40 -0)
entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEffectiveEntitlementEvent.java 166(+0 -166)
entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java 157(+101 -56)
entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java 115(+79 -36)
entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementContext.java 38(+30 -8)
entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java 241(+214 -27)
entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java 14(+9 -5)
entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionEvent.java 24(+8 -16)
entitlement/src/main/java/org/killbill/billing/entitlement/api/EntitlementDateHelper.java 35(+14 -21)
entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java 34(+15 -19)
entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java 81(+25 -56)
entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java 8(+5 -3)
entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java 44(+32 -12)
entitlement/src/main/java/org/killbill/billing/entitlement/dao/OptimizedProxyBlockingStateDao.java 5(+3 -2)
entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java 20(+14 -6)
entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/BlockingTransitionNotificationKey.java 34(+34 -0)
entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java 38(+33 -5)
entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java 6(+3 -3)
entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlement.java 85(+56 -29)
entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java 269(+205 -64)
entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionApi.java 168(+132 -36)
entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java 245(+208 -37)
entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEntitlementDateHelper.java 108(+50 -58)
entitlement/src/test/java/org/killbill/billing/entitlement/api/TestRegessionSubscriptionApi.java 104(+104 -0)
entitlement/src/test/java/org/killbill/billing/entitlement/dao/TestDefaultBlockingStateDao.java 16(+7 -9)
entitlement/src/test/java/org/killbill/billing/entitlement/engine/core/TestEntitlementUtils.java 78(+38 -40)
entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java 20(+15 -5)
invoice/src/main/java/org/killbill/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java 91(+0 -91)
invoice/src/main/java/org/killbill/billing/invoice/calculator/InvoiceCalculatorUtils.java 23(+13 -10)
invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java 62(+35 -27)
invoice/src/main/java/org/killbill/billing/invoice/generator/UsageInvoiceItemGenerator.java 20(+8 -12)
invoice/src/main/java/org/killbill/billing/invoice/notification/DefaultNextBillingDatePoster.java 19(+10 -9)
invoice/src/main/java/org/killbill/billing/invoice/notification/NextBillingDatePoster.java 10(+4 -6)
invoice/src/main/java/org/killbill/billing/invoice/notification/ParentInvoiceCommitmentPoster.java 6(+2 -4)
invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java 2(+1 -1)
invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableInArrear.java 23(+12 -11)
invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionConsumableInArrear.java 15(+8 -7)
invoice/src/test/java/org/killbill/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java 58(+24 -34)
invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java 36(+26 -10)
invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java 1(+0 -1)
invoice/src/test/java/org/killbill/billing/invoice/generator/TestFixedAndRecurringInvoiceItemGenerator.java 21(+8 -13)
invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java 2(+1 -1)
invoice/src/test/java/org/killbill/billing/invoice/usage/TestSubscriptionConsumableInArrear.java 11(+4 -7)
junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java 14(+8 -6)
junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java 18(+8 -10)
junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEventSet.java 47(+4 -43)
junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java 15(+7 -8)
junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java 15(+15 -0)
junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultBillingEvent.java 2(+1 -1)
junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java 14(+6 -8)
NEWS 3(+3 -0)
overdue/src/main/java/org/killbill/billing/overdue/notification/DefaultOverduePosterBase.java 11(+6 -5)
overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueAsyncBusPoster.java 11(+6 -5)
overdue/src/test/java/org/killbill/billing/overdue/notification/TestDefaultOverdueCheckPoster.java 2(+1 -1)
payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java 28(+17 -11)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlInitiated.java 10(+8 -2)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentStateControlContext.java 8(+4 -4)
payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java 24(+21 -3)
payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java 46(+33 -13)
payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentLeavingStateCallback.java 10(+4 -6)
payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java 29(+25 -4)
profiles/killbill/pom.xml 6(+6 -0)
profiles/killbill/src/test/java/org/killbill/billing/server/log/TestServerModuleNoDB.java 31(+25 -6)
profiles/killbill/src/test/java/org/killbill/billing/server/security/TestKillbillJdbcTenantRealm.java 8(+5 -3)
subscription/src/main/java/org/killbill/billing/subscription/alignment/MigrationPlanAligner.java 211(+0 -211)
subscription/src/main/java/org/killbill/billing/subscription/alignment/TimedMigration.java 126(+0 -126)
subscription/src/main/java/org/killbill/billing/subscription/api/migration/DefaultSubscriptionBaseMigrationApi.java 287(+0 -287)
subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java 11(+3 -8)
subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java 30(+19 -11)
subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimeline.java 22(+3 -19)
subscription/src/main/java/org/killbill/billing/subscription/api/transfer/BundleTransferData.java 33(+19 -14)
subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java 28(+8 -20)
subscription/src/main/java/org/killbill/billing/subscription/api/transfer/SubscriptionTransferData.java 79(+21 -58)
subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java 78(+36 -42)
subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java 109(+33 -76)
subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseTransitionDataIterator.java 15(+0 -15)
subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBuilder.java 23(+10 -13)
subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionEvents.java 26(+2 -24)
subscription/src/main/java/org/killbill/billing/subscription/engine/core/DefaultSubscriptionBaseService.java 6(+1 -5)
subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java 159(+15 -144)
subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionEventModelDao.java 46(+2 -44)
subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionModelDao.java 36(+19 -17)
subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionDao.java 9(+2 -7)
subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.java 11(+0 -11)
subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionSqlDao.java 11(+0 -11)
subscription/src/main/java/org/killbill/billing/subscription/events/EventBaseBuilder.java 13(+0 -13)
subscription/src/main/java/org/killbill/billing/subscription/events/phase/PhaseEventData.java 5(+1 -4)
subscription/src/main/java/org/killbill/billing/subscription/events/SubscriptionBaseEvent.java 2(+0 -2)
subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventBuilder.java 8(+0 -8)
subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventType.java 18(+0 -18)
subscription/src/main/java/org/killbill/billing/subscription/glue/DefaultSubscriptionModule.java 11(+0 -11)
subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.sql.stg 27(+0 -27)
subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionSqlDao.sql.stg 26(+2 -24)
subscription/src/test/java/org/killbill/billing/subscription/alignment/TestPlanAligner.java 30(+7 -23)
subscription/src/test/java/org/killbill/billing/subscription/alignment/TestTimedMigration.java 54(+0 -54)
subscription/src/test/java/org/killbill/billing/subscription/api/migration/TestMigration.java 304(+0 -304)
subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java 157(+4 -153)
subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestTransfer.java 61(+6 -55)
subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java 217(+7 -210)
subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCreate.java 47(+34 -13)
subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java 2(+1 -1)
subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiRecreate.java 114(+0 -114)
subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java 40(+2 -38)
subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoSql.java 10(+7 -3)
subscription/src/test/java/org/killbill/billing/subscription/glue/TestDefaultSubscriptionModuleNoDB.java 4(+2 -2)
subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java 11(+6 -5)
subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java 18(+14 -4)
tenant/src/test/java/org/killbill/billing/tenant/dao/TestNoCachingTenantBroadcastDao.java 32(+11 -21)
usage/pom.xml 5(+5 -0)
util/src/main/java/org/killbill/billing/util/callcontext/InternalCallContextFactory.java 120(+85 -35)
util/src/main/java/org/killbill/billing/util/customfield/api/DefaultCustomFieldUserApi.java 6(+3 -3)
util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java 12(+8 -4)
util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java 12(+8 -4)
util/src/main/java/org/killbill/billing/util/glue/NoCachingInternalCallContextFactoryProvider.java 30(+24 -6)
util/src/main/java/org/killbill/billing/util/timezone/DefaultAccountDateAndTimeZoneContext.java 79(+0 -79)
Details
.idea/compiler.xml 68(+34 -34)
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index 0fc88df..9b1b3d8 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -24,124 +24,124 @@
<processorPath useClasspath="true" />
<module name="currency" />
</profile>
- <profile default="false" name="Annotation profile for killbill-beatrix" enabled="true">
+ <profile default="false" name="Annotation profile for killbill-account" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="true" />
- <module name="killbill-beatrix" />
+ <module name="killbill-account" />
</profile>
- <profile default="false" name="Annotation profile for killbill-payment" enabled="true">
+ <profile default="false" name="Annotation profile for killbill-internal-api" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="true" />
- <module name="killbill-payment" />
+ <module name="killbill-internal-api" />
</profile>
- <profile default="false" name="Annotation profile for killbill-invoice" enabled="true">
+ <profile default="false" name="Annotation profile for killbill-beatrix" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="true" />
- <module name="killbill-invoice" />
+ <module name="killbill-beatrix" />
</profile>
- <profile default="false" name="Annotation profile for killbill-util" enabled="true">
+ <profile default="false" name="Annotation profile for killbill-catalog" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="true" />
- <module name="killbill-util" />
+ <module name="killbill-catalog" />
</profile>
- <profile default="false" name="Annotation profile for killbill-internal-api" enabled="true">
+ <profile default="false" name="Annotation profile for killbill-currency" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="true" />
- <module name="killbill-internal-api" />
+ <module name="killbill-currency" />
</profile>
- <profile default="false" name="Annotation profile for killbill-account" enabled="true">
+ <profile default="false" name="Annotation profile for killbill-entitlement" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="true" />
- <module name="killbill-account" />
+ <module name="killbill-entitlement" />
</profile>
- <profile default="false" name="Annotation profile for killbill-currency" enabled="true">
+ <profile default="false" name="Annotation profile for killbill-invoice" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="true" />
- <module name="killbill-currency" />
+ <module name="killbill-invoice" />
</profile>
- <profile default="false" name="Annotation profile for killbill-subscription" enabled="true">
+ <profile default="false" name="Annotation profile for killbill-jaxrs" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="true" />
- <module name="killbill-subscription" />
+ <module name="killbill-jaxrs" />
</profile>
- <profile default="false" name="Annotation profile for killbill-overdue" enabled="true">
+ <profile default="false" name="Annotation profile for killbill-junction" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="true" />
- <module name="killbill-overdue" />
+ <module name="killbill-junction" />
</profile>
- <profile default="false" name="Annotation profile for killbill-junction" enabled="true">
+ <profile default="false" name="Annotation profile for killbill-overdue" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="true" />
- <module name="killbill-junction" />
+ <module name="killbill-overdue" />
</profile>
- <profile default="false" name="Annotation profile for killbill-tenant" enabled="true">
+ <profile default="false" name="Annotation profile for killbill-payment" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="true" />
- <module name="killbill-tenant" />
+ <module name="killbill-payment" />
</profile>
- <profile default="false" name="Annotation profile for killbill-jaxrs" enabled="true">
+ <profile default="false" name="Annotation profile for killbill-subscription" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="true" />
- <module name="killbill-jaxrs" />
+ <module name="killbill-subscription" />
</profile>
- <profile default="false" name="Annotation profile for killbill-profiles-killpay" enabled="true">
+ <profile default="false" name="Annotation profile for killbill-tenant" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="true" />
- <module name="killbill-profiles-killpay" />
+ <module name="killbill-tenant" />
</profile>
- <profile default="false" name="Annotation profile for killbill-profiles-killbill" enabled="true">
+ <profile default="false" name="Annotation profile for killbill-usage" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="true" />
- <module name="killbill-profiles-killbill" />
+ <module name="killbill-usage" />
</profile>
- <profile default="false" name="Annotation profile for killbill-usage" enabled="true">
+ <profile default="false" name="Annotation profile for killbill-util" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="true" />
- <module name="killbill-usage" />
+ <module name="killbill-util" />
</profile>
- <profile default="false" name="Annotation profile for killbill-catalog" enabled="true">
+ <profile default="false" name="Annotation profile for killbill-profiles-killbill" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="true" />
- <module name="killbill-catalog" />
+ <module name="killbill-profiles-killbill" />
</profile>
- <profile default="false" name="Annotation profile for killbill-entitlement" enabled="true">
+ <profile default="false" name="Annotation profile for killbill-profiles-killpay" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="true" />
- <module name="killbill-entitlement" />
+ <module name="killbill-profiles-killpay" />
</profile>
</annotationProcessing>
<bytecodeTargetLevel>
.idea/modules.xml 3(+1 -2)
diff --git a/.idea/modules.xml b/.idea/modules.xml
index c733a45..560d78f 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -23,5 +23,4 @@
<module fileurl="file://$PROJECT_DIR$/util/killbill-util.iml" filepath="$PROJECT_DIR$/util/killbill-util.iml" />
</modules>
</component>
-</project>
-
+</project>
\ No newline at end of file
diff --git a/account/src/main/java/org/killbill/billing/account/api/DefaultAccount.java b/account/src/main/java/org/killbill/billing/account/api/DefaultAccount.java
index b367197..637907d 100644
--- a/account/src/main/java/org/killbill/billing/account/api/DefaultAccount.java
+++ b/account/src/main/java/org/killbill/billing/account/api/DefaultAccount.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -27,6 +27,7 @@ import org.joda.time.DateTimeZone;
import org.killbill.billing.account.dao.AccountModelDao;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.entity.EntityBase;
+import org.killbill.billing.util.account.AccountDateTimeUtils;
import static org.killbill.billing.account.api.DefaultMutableAccountData.DEFAULT_BILLING_CYCLE_DAY_LOCAL;
@@ -368,6 +369,16 @@ public class DefaultAccount extends EntityBase implements Account {
}
@Override
+ public DateTimeZone getFixedOffsetTimeZone() {
+ return AccountDateTimeUtils.getFixedOffsetTimeZone(this);
+ }
+
+ @Override
+ public DateTime getReferenceTime() {
+ return AccountDateTimeUtils.getReferenceDateTime(this);
+ }
+
+ @Override
public String toString() {
return "DefaultAccount [externalKey=" + externalKey +
", email=" + email +
diff --git a/account/src/main/java/org/killbill/billing/account/api/DefaultChangedField.java b/account/src/main/java/org/killbill/billing/account/api/DefaultChangedField.java
index 7ee0e32..8b11cfb 100644
--- a/account/src/main/java/org/killbill/billing/account/api/DefaultChangedField.java
+++ b/account/src/main/java/org/killbill/billing/account/api/DefaultChangedField.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -17,7 +19,6 @@
package org.killbill.billing.account.api;
import org.joda.time.DateTime;
-
import org.killbill.billing.events.ChangedField;
import com.fasterxml.jackson.annotation.JsonCreator;
@@ -41,12 +42,6 @@ public class DefaultChangedField implements ChangedField {
this.newValue = newValue;
}
- public DefaultChangedField(final String fieldName,
- final String oldValue,
- final String newValue) {
- this(fieldName, oldValue, newValue, new DateTime());
- }
-
@Override
public String getFieldName() {
return fieldName;
@@ -124,5 +119,4 @@ public class DefaultChangedField implements ChangedField {
}
return true;
}
-
}
diff --git a/account/src/main/java/org/killbill/billing/account/api/DefaultImmutableAccountData.java b/account/src/main/java/org/killbill/billing/account/api/DefaultImmutableAccountData.java
index 73e5bd6..4090248 100644
--- a/account/src/main/java/org/killbill/billing/account/api/DefaultImmutableAccountData.java
+++ b/account/src/main/java/org/killbill/billing/account/api/DefaultImmutableAccountData.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -19,8 +19,10 @@ package org.killbill.billing.account.api;
import java.util.UUID;
+import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.util.account.AccountDateTimeUtils;
public class DefaultImmutableAccountData implements ImmutableAccountData {
@@ -30,19 +32,24 @@ public class DefaultImmutableAccountData implements ImmutableAccountData {
private final DateTimeZone dateTimeZone;
private final UUID parentAccountId;
private final boolean isPaymentDelegatedToParent;
+ private final DateTimeZone fixedOffsetDateTimeZone;
+ private final DateTime referenceTime;
- public DefaultImmutableAccountData(final UUID id, final String externalKey, final Currency currency, final DateTimeZone dateTimeZone,
- final UUID parentAccountId, final boolean isPaymentDelegatedToParent) {
+ public DefaultImmutableAccountData(final UUID id, final String externalKey, final Currency currency, final DateTimeZone dateTimeZone, final DateTimeZone fixedOffsetDateTimeZone, final DateTime referenceTime, final UUID parentAccountId, final boolean isPaymentDelegatedToParent) {
this.id = id;
this.externalKey = externalKey;
this.currency = currency;
this.dateTimeZone = dateTimeZone;
+ this.fixedOffsetDateTimeZone = fixedOffsetDateTimeZone;
+ this.referenceTime = referenceTime;
this.parentAccountId = parentAccountId;
this.isPaymentDelegatedToParent = isPaymentDelegatedToParent;
}
public DefaultImmutableAccountData(final Account account) {
- this(account.getId(), account.getExternalKey(), account.getCurrency(), account.getTimeZone(), account.getParentAccountId(), account.isPaymentDelegatedToParent());
+ this(account.getId(), account.getExternalKey(), account.getCurrency(), account.getTimeZone(),
+ AccountDateTimeUtils.getFixedOffsetTimeZone(account), AccountDateTimeUtils.getReferenceDateTime(account),
+ account.getParentAccountId(), account.isPaymentDelegatedToParent());
}
@Override
@@ -75,4 +82,65 @@ public class DefaultImmutableAccountData implements ImmutableAccountData {
return isPaymentDelegatedToParent;
}
+ public DateTimeZone getFixedOffsetTimeZone() {
+ return fixedOffsetDateTimeZone;
+ }
+
+ @Override
+ public DateTime getReferenceTime() {
+ return referenceTime;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("DefaultImmutableAccountData{");
+ sb.append("id=").append(id);
+ sb.append(", externalKey='").append(externalKey).append('\'');
+ sb.append(", currency=").append(currency);
+ sb.append(", dateTimeZone=").append(dateTimeZone);
+ sb.append(", fixedOffsetDateTimeZone=").append(fixedOffsetDateTimeZone);
+ sb.append(", referenceTime=").append(referenceTime);
+ 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 DefaultImmutableAccountData that = (DefaultImmutableAccountData) o;
+
+ if (id != null ? !id.equals(that.id) : that.id != null) {
+ return false;
+ }
+ if (externalKey != null ? !externalKey.equals(that.externalKey) : that.externalKey != null) {
+ return false;
+ }
+ if (currency != that.currency) {
+ return false;
+ }
+ if (dateTimeZone != null ? !dateTimeZone.equals(that.dateTimeZone) : that.dateTimeZone != null) {
+ return false;
+ }
+ if (fixedOffsetDateTimeZone != null ? !fixedOffsetDateTimeZone.equals(that.fixedOffsetDateTimeZone) : that.fixedOffsetDateTimeZone != null) {
+ return false;
+ }
+ return referenceTime != null ? referenceTime.compareTo(that.referenceTime) == 0 : that.referenceTime == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = id != null ? id.hashCode() : 0;
+ result = 31 * result + (externalKey != null ? externalKey.hashCode() : 0);
+ result = 31 * result + (currency != null ? currency.hashCode() : 0);
+ result = 31 * result + (dateTimeZone != null ? dateTimeZone.hashCode() : 0);
+ result = 31 * result + (fixedOffsetDateTimeZone != null ? fixedOffsetDateTimeZone.hashCode() : 0);
+ result = 31 * result + (referenceTime != null ? referenceTime.hashCode() : 0);
+ return result;
+ }
}
diff --git a/account/src/main/java/org/killbill/billing/account/api/svcs/DefaultAccountInternalApi.java b/account/src/main/java/org/killbill/billing/account/api/svcs/DefaultAccountInternalApi.java
index 0903c13..57203fc 100644
--- a/account/src/main/java/org/killbill/billing/account/api/svcs/DefaultAccountInternalApi.java
+++ b/account/src/main/java/org/killbill/billing/account/api/svcs/DefaultAccountInternalApi.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -28,9 +30,9 @@ import org.killbill.billing.account.api.AccountApiException;
import org.killbill.billing.account.api.AccountEmail;
import org.killbill.billing.account.api.AccountInternalApi;
import org.killbill.billing.account.api.DefaultAccountEmail;
-import org.killbill.billing.account.api.DefaultImmutableAccountData;
import org.killbill.billing.account.api.DefaultMutableAccountData;
import org.killbill.billing.account.api.ImmutableAccountData;
+import org.killbill.billing.account.api.ImmutableAccountInternalApi;
import org.killbill.billing.account.api.MutableAccountData;
import org.killbill.billing.account.api.user.DefaultAccountApiBase;
import org.killbill.billing.account.dao.AccountDao;
@@ -43,7 +45,6 @@ import org.killbill.billing.util.cache.Cachable.CacheType;
import org.killbill.billing.util.cache.CacheController;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
import org.killbill.billing.util.cache.CacheLoaderArgument;
-import org.killbill.billing.util.cache.ImmutableAccountCacheLoader.LoaderCallback;
import org.killbill.billing.util.dao.NonEntityDao;
import com.google.common.base.Function;
@@ -52,21 +53,18 @@ import com.google.common.collect.ImmutableList;
public class DefaultAccountInternalApi extends DefaultAccountApiBase implements AccountInternalApi {
+ private final ImmutableAccountInternalApi immutableAccountInternalApi;
private final AccountDao accountDao;
- private final CacheControllerDispatcher cacheControllerDispatcher;
- private final CacheController accountCacheController;
private final CacheController bcdCacheController;
- private final NonEntityDao nonEntityDao;
@Inject
- public DefaultAccountInternalApi(final AccountDao accountDao,
+ public DefaultAccountInternalApi(final ImmutableAccountInternalApi immutableAccountInternalApi,
+ final AccountDao accountDao,
final NonEntityDao nonEntityDao,
final CacheControllerDispatcher cacheControllerDispatcher) {
super(accountDao, nonEntityDao, cacheControllerDispatcher);
+ this.immutableAccountInternalApi = immutableAccountInternalApi;
this.accountDao = accountDao;
- this.nonEntityDao = nonEntityDao;
- this.cacheControllerDispatcher = cacheControllerDispatcher;
- this.accountCacheController = cacheControllerDispatcher.getCacheController(CacheType.ACCOUNT_IMMUTABLE);
this.bcdCacheController = cacheControllerDispatcher.getCacheController(CacheType.ACCOUNT_BCD);
}
@@ -142,14 +140,12 @@ public class DefaultAccountInternalApi extends DefaultAccountApiBase implements
@Override
public ImmutableAccountData getImmutableAccountDataById(final UUID accountId, final InternalTenantContext context) throws AccountApiException {
- final Long recordId = nonEntityDao.retrieveRecordIdFromObject(accountId, ObjectType.ACCOUNT, cacheControllerDispatcher.getCacheController(CacheType.RECORD_ID));
- return getImmutableAccountDataByRecordId(recordId, context);
+ return immutableAccountInternalApi.getImmutableAccountDataById(accountId, context);
}
@Override
public ImmutableAccountData getImmutableAccountDataByRecordId(final Long recordId, final InternalTenantContext context) throws AccountApiException {
- final CacheLoaderArgument arg = createImmutableAccountCacheLoaderArgument(context);
- return (ImmutableAccountData) accountCacheController.get(recordId, arg);
+ return immutableAccountInternalApi.getImmutableAccountDataByRecordId(recordId, context);
}
private AccountModelDao getAccountModelDaoByRecordId(final Long recordId, final InternalTenantContext context) throws AccountApiException {
@@ -165,20 +161,6 @@ public class DefaultAccountInternalApi extends DefaultAccountApiBase implements
return bcd != null ? bcd : DefaultMutableAccountData.DEFAULT_BILLING_CYCLE_DAY_LOCAL;
}
- private CacheLoaderArgument createImmutableAccountCacheLoaderArgument(final InternalTenantContext context) {
- final LoaderCallback loaderCallback = new LoaderCallback() {
- @Override
- public Object loadAccount(final Long recordId, final InternalTenantContext context) {
- final Account account = getAccountByRecordIdInternal(recordId, context);
- return account != null ? new DefaultImmutableAccountData(account) : null;
- }
- };
- final Object[] args = new Object[1];
- args[0] = loaderCallback;
- final ObjectType irrelevant = null;
- return new CacheLoaderArgument(irrelevant, args, context);
- }
-
private CacheLoaderArgument createBCDCacheLoaderArgument(final InternalTenantContext context) {
final AccountBCDCacheLoader.LoaderCallback loaderCallback = new AccountBCDCacheLoader.LoaderCallback() {
@Override
diff --git a/account/src/main/java/org/killbill/billing/account/api/svcs/DefaultImmutableAccountInternalApi.java b/account/src/main/java/org/killbill/billing/account/api/svcs/DefaultImmutableAccountInternalApi.java
new file mode 100644
index 0000000..250d47a
--- /dev/null
+++ b/account/src/main/java/org/killbill/billing/account/api/svcs/DefaultImmutableAccountInternalApi.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2016 Groupon, Inc
+ * Copyright 2016 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.account.api.svcs;
+
+import java.util.UUID;
+
+import org.killbill.billing.ObjectType;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountApiException;
+import org.killbill.billing.account.api.DefaultAccount;
+import org.killbill.billing.account.api.DefaultImmutableAccountData;
+import org.killbill.billing.account.api.ImmutableAccountData;
+import org.killbill.billing.account.api.ImmutableAccountInternalApi;
+import org.killbill.billing.account.dao.AccountModelDao;
+import org.killbill.billing.account.dao.AccountSqlDao;
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.killbill.billing.util.cache.CacheController;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.cache.CacheLoaderArgument;
+import org.killbill.billing.util.cache.ImmutableAccountCacheLoader.LoaderCallback;
+import org.killbill.billing.util.dao.NonEntityDao;
+import org.killbill.billing.util.entity.dao.EntitySqlDao;
+import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
+import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
+import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
+import org.killbill.clock.Clock;
+import org.skife.jdbi.v2.IDBI;
+
+import com.google.inject.Inject;
+
+public class DefaultImmutableAccountInternalApi implements ImmutableAccountInternalApi {
+
+ private final EntitySqlDaoTransactionalJdbiWrapper transactionalSqlDao;
+ private final NonEntityDao nonEntityDao;
+ private final CacheControllerDispatcher cacheControllerDispatcher;
+ private final CacheController accountCacheController;
+
+ @Inject
+ public DefaultImmutableAccountInternalApi(final IDBI dbi,
+ final Clock clock,
+ final NonEntityDao nonEntityDao,
+ final CacheControllerDispatcher cacheControllerDispatcher) {
+ // This API will directly issue queries instead of relying on the DAO (introduced to avoid Guice circular dependencies with InternalCallContextFactory)
+ this.transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao, null);
+ this.nonEntityDao = nonEntityDao;
+ this.cacheControllerDispatcher = cacheControllerDispatcher;
+ this.accountCacheController = cacheControllerDispatcher.getCacheController(CacheType.ACCOUNT_IMMUTABLE);
+ }
+
+ @Override
+ public ImmutableAccountData getImmutableAccountDataById(final UUID accountId, final InternalTenantContext context) throws AccountApiException {
+ final Long recordId = nonEntityDao.retrieveRecordIdFromObject(accountId, ObjectType.ACCOUNT, cacheControllerDispatcher.getCacheController(CacheType.RECORD_ID));
+ return getImmutableAccountDataByRecordId(recordId, context);
+ }
+
+ @Override
+ public ImmutableAccountData getImmutableAccountDataByRecordId(final Long recordId, final InternalTenantContext context) throws AccountApiException {
+ final CacheLoaderArgument arg = createImmutableAccountCacheLoaderArgument(context);
+ return (ImmutableAccountData) accountCacheController.get(recordId, arg);
+ }
+
+ private CacheLoaderArgument createImmutableAccountCacheLoaderArgument(final InternalTenantContext context) {
+ final LoaderCallback loaderCallback = new LoaderCallback() {
+ @Override
+ public Object loadAccount(final Long recordId, final InternalTenantContext context) {
+ final Account account = getAccountByRecordIdInternal(recordId, context);
+ return account != null ? new DefaultImmutableAccountData(account) : null;
+ }
+ };
+
+ final Object[] args = {loaderCallback};
+ return new CacheLoaderArgument(null, args, context);
+ }
+
+ private Account getAccountByRecordIdInternal(final Long recordId, final InternalTenantContext context) {
+ final AccountModelDao accountModelDao = transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<AccountModelDao>() {
+
+ @Override
+ public AccountModelDao inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+ final EntitySqlDao<AccountModelDao, Account> transactional = entitySqlDaoWrapperFactory.become(AccountSqlDao.class);
+ return transactional.getByRecordId(recordId, context);
+ }
+ });
+
+ return accountModelDao != null ? new DefaultAccount(accountModelDao) : null;
+ }
+}
diff --git a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountChangeEvent.java b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountChangeEvent.java
index 9ee7111..d68f0dc 100644
--- a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountChangeEvent.java
+++ b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountChangeEvent.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -17,9 +19,11 @@
package org.killbill.billing.account.api.user;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.UUID;
+import org.joda.time.DateTime;
import org.killbill.billing.account.api.DefaultChangedField;
import org.killbill.billing.account.dao.AccountModelDao;
import org.killbill.billing.events.AccountChangeInternalEvent;
@@ -47,10 +51,16 @@ public class DefaultAccountChangeEvent extends BusEventBase implements AccountCh
this.changedFields = changedFields;
}
- public DefaultAccountChangeEvent(final UUID id, final AccountModelDao oldData, final AccountModelDao newData, final Long searchKey1, final Long searchKey2, final UUID userToken) {
+ public DefaultAccountChangeEvent(final UUID id,
+ final AccountModelDao oldData,
+ final AccountModelDao newData,
+ final Long searchKey1,
+ final Long searchKey2,
+ final UUID userToken,
+ final DateTime changeDate) {
super(searchKey1, searchKey2, userToken);
this.accountId = id;
- this.changedFields = calculateChangedFields(oldData, newData);
+ this.changedFields = calculateChangedFields(oldData, newData, changeDate);
}
@JsonIgnore
@@ -73,7 +83,7 @@ public class DefaultAccountChangeEvent extends BusEventBase implements AccountCh
@JsonIgnore
@Override
public boolean hasChanges() {
- return (changedFields.size() > 0);
+ return (!changedFields.isEmpty());
}
@Override
@@ -116,57 +126,56 @@ public class DefaultAccountChangeEvent extends BusEventBase implements AccountCh
return true;
}
- private List<ChangedField> calculateChangedFields(final AccountModelDao oldData, final AccountModelDao newData) {
-
+ private List<ChangedField> calculateChangedFields(final AccountModelDao oldData, final AccountModelDao newData, final DateTime changeDate) {
final List<ChangedField> tmpChangedFields = new ArrayList<ChangedField>();
addIfValueChanged(tmpChangedFields, "externalKey",
- oldData.getExternalKey(), newData.getExternalKey());
+ oldData.getExternalKey(), newData.getExternalKey(), changeDate);
addIfValueChanged(tmpChangedFields, "email",
- oldData.getEmail(), newData.getEmail());
+ oldData.getEmail(), newData.getEmail(), changeDate);
addIfValueChanged(tmpChangedFields, "firstName",
- oldData.getName(), newData.getName());
+ oldData.getName(), newData.getName(), changeDate);
addIfValueChanged(tmpChangedFields, "currency",
(oldData.getCurrency() != null) ? oldData.getCurrency().toString() : null,
- (newData.getCurrency() != null) ? newData.getCurrency().toString() : null);
+ (newData.getCurrency() != null) ? newData.getCurrency().toString() : null, changeDate);
addIfValueChanged(tmpChangedFields,
"billCycleDayLocal",
- String.valueOf(oldData.getBillingCycleDayLocal()), String.valueOf(newData.getBillingCycleDayLocal()));
+ String.valueOf(oldData.getBillingCycleDayLocal()), String.valueOf(newData.getBillingCycleDayLocal()), changeDate);
addIfValueChanged(tmpChangedFields, "paymentMethodId",
(oldData.getPaymentMethodId() != null) ? oldData.getPaymentMethodId().toString() : null,
- (newData.getPaymentMethodId() != null) ? newData.getPaymentMethodId().toString() : null);
+ (newData.getPaymentMethodId() != null) ? newData.getPaymentMethodId().toString() : null, changeDate);
- addIfValueChanged(tmpChangedFields, "locale", oldData.getLocale(), newData.getLocale());
+ addIfValueChanged(tmpChangedFields, "locale", oldData.getLocale(), newData.getLocale(), changeDate);
addIfValueChanged(tmpChangedFields, "timeZone",
(oldData.getTimeZone() == null) ? null : oldData.getTimeZone().toString(),
- (newData.getTimeZone() == null) ? null : newData.getTimeZone().toString());
+ (newData.getTimeZone() == null) ? null : newData.getTimeZone().toString(), changeDate);
- addIfValueChanged(tmpChangedFields, "address1", oldData.getAddress1(), newData.getAddress1());
- addIfValueChanged(tmpChangedFields, "address2", oldData.getAddress2(), newData.getAddress2());
- addIfValueChanged(tmpChangedFields, "city", oldData.getCity(), newData.getCity());
- addIfValueChanged(tmpChangedFields, "stateOrProvince", oldData.getStateOrProvince(), newData.getStateOrProvince());
- addIfValueChanged(tmpChangedFields, "country", oldData.getCountry(), newData.getCountry());
- addIfValueChanged(tmpChangedFields, "postalCode", oldData.getPostalCode(), newData.getPostalCode());
- addIfValueChanged(tmpChangedFields, "phone", oldData.getPhone(), newData.getPhone());
+ addIfValueChanged(tmpChangedFields, "address1", oldData.getAddress1(), newData.getAddress1(), changeDate);
+ addIfValueChanged(tmpChangedFields, "address2", oldData.getAddress2(), newData.getAddress2(), changeDate);
+ addIfValueChanged(tmpChangedFields, "city", oldData.getCity(), newData.getCity(), changeDate);
+ addIfValueChanged(tmpChangedFields, "stateOrProvince", oldData.getStateOrProvince(), newData.getStateOrProvince(), changeDate);
+ addIfValueChanged(tmpChangedFields, "country", oldData.getCountry(), newData.getCountry(), changeDate);
+ addIfValueChanged(tmpChangedFields, "postalCode", oldData.getPostalCode(), newData.getPostalCode(), changeDate);
+ addIfValueChanged(tmpChangedFields, "phone", oldData.getPhone(), newData.getPhone(), changeDate);
return tmpChangedFields;
}
- private void addIfValueChanged(final List<ChangedField> inputList, final String key, final String oldData, final String newData) {
+ private void addIfValueChanged(final Collection<ChangedField> inputList, final String key, final String oldData, final String newData, final DateTime changeDate) {
// If both null => no changes
if (newData == null && oldData == null) {
// If only one is null
} else if (newData == null || oldData == null) {
- inputList.add(new DefaultChangedField(key, oldData, newData));
+ inputList.add(new DefaultChangedField(key, oldData, newData, changeDate));
// If neither are null we can safely compare values
} else if (!newData.equals(oldData)) {
- inputList.add(new DefaultChangedField(key, oldData, newData));
+ inputList.add(new DefaultChangedField(key, oldData, newData, changeDate));
}
}
}
diff --git a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java
index e5f0d5d..1f1ad92 100644
--- a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java
+++ b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java
@@ -67,13 +67,13 @@ public class DefaultAccountUserApi extends DefaultAccountApiBase implements Acco
@Override
public Account getAccountByKey(final String key, final TenantContext context) throws AccountApiException {
- final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(context);
+ final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context);
return getAccountByKey(key, internalTenantContext);
}
@Override
public Account getAccountById(final UUID id, final TenantContext context) throws AccountApiException {
- final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(context);
+ final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(id, context);
return getAccountById(id, internalTenantContext);
}
@@ -85,7 +85,7 @@ public class DefaultAccountUserApi extends DefaultAccountApiBase implements Acco
throw new AccountApiException(ErrorCode.ACCOUNT_ALREADY_EXISTS, data.getExternalKey());
}
- final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(context);
+ final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(context);
if (data.getParentAccountId() != null) {
// verify that parent account exists if parentAccountId is not null
@@ -93,7 +93,7 @@ public class DefaultAccountUserApi extends DefaultAccountApiBase implements Acco
}
final AccountModelDao account = new AccountModelDao(data);
- accountDao.create(account, internalContext);
+ accountDao.create(account, internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(context));
return new DefaultAccount(account);
}
@@ -105,7 +105,7 @@ public class DefaultAccountUserApi extends DefaultAccountApiBase implements Acco
new SourcePaginationBuilder<AccountModelDao, AccountApiException>() {
@Override
public Pagination<AccountModelDao> build() {
- return accountDao.searchAccounts(searchKey, offset, limit, internalCallContextFactory.createInternalTenantContext(context));
+ return accountDao.searchAccounts(searchKey, offset, limit, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
}
},
new Function<AccountModelDao, Account>() {
@@ -123,7 +123,7 @@ public class DefaultAccountUserApi extends DefaultAccountApiBase implements Acco
new SourcePaginationBuilder<AccountModelDao, AccountApiException>() {
@Override
public Pagination<AccountModelDao> build() {
- return accountDao.get(offset, limit, internalCallContextFactory.createInternalTenantContext(context));
+ return accountDao.get(offset, limit, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
}
},
new Function<AccountModelDao, Account>() {
@@ -137,7 +137,7 @@ public class DefaultAccountUserApi extends DefaultAccountApiBase implements Acco
@Override
public UUID getIdFromKey(final String externalKey, final TenantContext context) throws AccountApiException {
- return accountDao.getIdFromKey(externalKey, internalCallContextFactory.createInternalTenantContext(context));
+ return accountDao.getIdFromKey(externalKey, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
}
@Override
@@ -178,7 +178,7 @@ public class DefaultAccountUserApi extends DefaultAccountApiBase implements Acco
@Override
public List<AccountEmail> getEmails(final UUID accountId, final TenantContext context) {
- return ImmutableList.<AccountEmail>copyOf(Collections2.transform(accountDao.getEmailsByAccountId(accountId, internalCallContextFactory.createInternalTenantContext(context)),
+ return ImmutableList.<AccountEmail>copyOf(Collections2.transform(accountDao.getEmailsByAccountId(accountId, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context)),
new Function<AccountEmailModelDao, AccountEmail>() {
@Override
public AccountEmail apply(final AccountEmailModelDao input) {
@@ -199,7 +199,7 @@ public class DefaultAccountUserApi extends DefaultAccountApiBase implements Acco
@Override
public List<Account> getChildrenAccounts(final UUID parentAccountId, final TenantContext context) throws AccountApiException {
- return ImmutableList.<Account>copyOf(Collections2.transform(accountDao.getAccountsByParentId(parentAccountId, internalCallContextFactory.createInternalTenantContext(context)),
+ return ImmutableList.<Account>copyOf(Collections2.transform(accountDao.getAccountsByParentId(parentAccountId, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context)),
new Function<AccountModelDao, Account>() {
@Override
public Account apply(final AccountModelDao input) {
diff --git a/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java b/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
index e713194..daf499e 100644
--- a/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
+++ b/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -57,13 +59,15 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
private final PersistentBus eventBus;
private final InternalCallContextFactory internalCallContextFactory;
+ private final Clock clock;
@Inject
public DefaultAccountDao(final IDBI dbi, final PersistentBus eventBus, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher,
final InternalCallContextFactory internalCallContextFactory, final NonEntityDao nonEntityDao) {
- super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao), AccountSqlDao.class);
+ super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory), AccountSqlDao.class);
this.eventBus = eventBus;
this.internalCallContextFactory = internalCallContextFactory;
+ this.clock = clock;
}
@Override
@@ -157,8 +161,8 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
specifiedAccount,
context.getAccountRecordId(),
context.getTenantRecordId(),
- context.getUserToken()
- );
+ context.getUserToken(),
+ clock.getUTCNow());
try {
eventBus.postFromTransaction(changeEvent, entitySqlDaoWrapperFactory.getHandle().getConnection());
} catch (final EventBusException e) {
@@ -195,8 +199,8 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
final AccountChangeInternalEvent changeEvent = new DefaultAccountChangeEvent(accountId, currentAccount, account,
context.getAccountRecordId(),
context.getTenantRecordId(),
- context.getUserToken()
- );
+ context.getUserToken(),
+ clock.getUTCNow());
try {
eventBus.postFromTransaction(changeEvent, entitySqlDaoWrapperFactory.getHandle().getConnection());
diff --git a/account/src/main/java/org/killbill/billing/account/glue/DefaultAccountModule.java b/account/src/main/java/org/killbill/billing/account/glue/DefaultAccountModule.java
index 705ae60..c5931f6 100644
--- a/account/src/main/java/org/killbill/billing/account/glue/DefaultAccountModule.java
+++ b/account/src/main/java/org/killbill/billing/account/glue/DefaultAccountModule.java
@@ -22,7 +22,9 @@ import org.killbill.billing.account.api.AccountInternalApi;
import org.killbill.billing.account.api.AccountService;
import org.killbill.billing.account.api.AccountUserApi;
import org.killbill.billing.account.api.DefaultAccountService;
+import org.killbill.billing.account.api.ImmutableAccountInternalApi;
import org.killbill.billing.account.api.svcs.DefaultAccountInternalApi;
+import org.killbill.billing.account.api.svcs.DefaultImmutableAccountInternalApi;
import org.killbill.billing.account.api.user.DefaultAccountUserApi;
import org.killbill.billing.account.dao.AccountDao;
import org.killbill.billing.account.dao.DefaultAccountDao;
@@ -51,6 +53,7 @@ public class DefaultAccountModule extends KillBillModule implements AccountModul
@Override
public void installInternalApi() {
bind(AccountInternalApi.class).to(DefaultAccountInternalApi.class).asEagerSingleton();
+ bind(ImmutableAccountInternalApi.class).to(DefaultImmutableAccountInternalApi.class).asEagerSingleton();
}
private void installAccountService() {
diff --git a/account/src/test/java/org/killbill/billing/account/AccountTestSuiteWithEmbeddedDB.java b/account/src/test/java/org/killbill/billing/account/AccountTestSuiteWithEmbeddedDB.java
index b441e7b..1e146a5 100644
--- a/account/src/test/java/org/killbill/billing/account/AccountTestSuiteWithEmbeddedDB.java
+++ b/account/src/test/java/org/killbill/billing/account/AccountTestSuiteWithEmbeddedDB.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -16,16 +18,13 @@
package org.killbill.billing.account;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.BeforeMethod;
-
import org.killbill.billing.GuicyKillbillTestSuiteWithEmbeddedDB;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountApiException;
+import org.killbill.billing.account.api.AccountData;
import org.killbill.billing.account.api.AccountUserApi;
import org.killbill.billing.account.dao.AccountDao;
import org.killbill.billing.account.glue.TestAccountModuleWithEmbeddedDB;
-import org.killbill.bus.api.PersistentBus;
-import org.killbill.clock.Clock;
import org.killbill.billing.util.audit.dao.AuditDao;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
import org.killbill.billing.util.customfield.dao.CustomFieldDao;
@@ -33,6 +32,11 @@ import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.tag.api.user.TagEventBuilder;
import org.killbill.billing.util.tag.dao.TagDao;
import org.killbill.billing.util.tag.dao.TagDefinitionDao;
+import org.killbill.bus.api.PersistentBus;
+import org.killbill.clock.Clock;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
import com.google.inject.Guice;
import com.google.inject.Inject;
@@ -80,4 +84,12 @@ public abstract class AccountTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
public void afterMethod() throws Exception {
bus.stop();
}
+
+ protected Account createAccount(final AccountData accountData) throws AccountApiException {
+ final Account account = accountUserApi.createAccount(accountData, callContext);
+
+ refreshCallContext(account.getId());
+
+ return account;
+ }
}
diff --git a/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApi.java b/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApi.java
index 1d498ee..05980b0 100644
--- a/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApi.java
+++ b/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApi.java
@@ -51,7 +51,7 @@ public class TestDefaultAccountUserApi extends AccountTestSuiteWithEmbeddedDB {
final AccountModelDao accountModelDao = createTestAccount();
final AccountData defaultAccount = new DefaultAccount(accountModelDao);
- final Account account = accountUserApi.createAccount(defaultAccount, callContext);
+ final Account account = createAccount(defaultAccount);
await().atMost(10, SECONDS).until(new Callable<Boolean>() {
@Override
@@ -68,7 +68,7 @@ public class TestDefaultAccountUserApi extends AccountTestSuiteWithEmbeddedDB {
@Test(groups = "slow", description = "Test Account update with null values")
public void testShouldBeAbleToPassNullForSomeFieldsToAvoidUpdate() throws Exception {
- final Account account = accountUserApi.createAccount(new DefaultAccount(createTestAccount()), callContext);
+ final Account account = createAccount(new DefaultAccount(createTestAccount()));
// Update the address and leave other fields null
final MutableAccountData mutableAccountData = new DefaultMutableAccountData(null, null, null, 0, null, null, false, 0, null,
@@ -89,7 +89,7 @@ public class TestDefaultAccountUserApi extends AccountTestSuiteWithEmbeddedDB {
@Test(groups = "slow", expectedExceptions = IllegalArgumentException.class, description = "Test updating Account BCD does throws an exception")
public void testShouldntBeAbleToUpdateBillCycleDay() throws Exception {
- final Account account = accountUserApi.createAccount(new DefaultAccount(createTestAccount()), callContext);
+ final Account account = createAccount(new DefaultAccount(createTestAccount()));
final MutableAccountData otherAccount = new DefaultAccount(account.getId(), account).toMutableAccountData();
otherAccount.setBillCycleDayLocal(account.getBillCycleDayLocal() + 2);
@@ -99,7 +99,7 @@ public class TestDefaultAccountUserApi extends AccountTestSuiteWithEmbeddedDB {
@Test(groups = "slow", expectedExceptions = IllegalArgumentException.class, description = "Test updating Account currency throws an exception")
public void testShouldntBeAbleToUpdateCurrency() throws Exception {
- final Account account = accountUserApi.createAccount(new DefaultAccount(createTestAccount()), callContext);
+ final Account account = createAccount(new DefaultAccount(createTestAccount()));
final MutableAccountData otherAccount = new DefaultAccount(account.getId(), account).toMutableAccountData();
otherAccount.setCurrency(Currency.GBP);
@@ -109,7 +109,7 @@ public class TestDefaultAccountUserApi extends AccountTestSuiteWithEmbeddedDB {
@Test(groups = "slow", expectedExceptions = IllegalArgumentException.class, description = "Test updating Account externalKey throws an exception")
public void testShouldntBeAbleToUpdateExternalKey() throws Exception {
- final Account account = accountUserApi.createAccount(new DefaultAccount(createTestAccount()), callContext);
+ final Account account = createAccount(new DefaultAccount(createTestAccount()));
final MutableAccountData otherAccount = new DefaultAccount(account.getId(), account).toMutableAccountData();
otherAccount.setExternalKey(UUID.randomUUID().toString());
diff --git a/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java b/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java
index 6d48b71..43bcb2f 100644
--- a/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java
+++ b/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -52,7 +52,7 @@ public class TestDefaultAccountUserApiWithMocks extends AccountTestSuiteNoDB {
@BeforeMethod(groups = "fast")
public void setUp() throws Exception {
- accountDao = new MockAccountDao(Mockito.mock(PersistentBus.class));
+ accountDao = new MockAccountDao(Mockito.mock(PersistentBus.class), clock);
accountUserApi = new DefaultAccountUserApi(accountDao, nonEntityDao, controllerDispatcher, internalFactory);
}
diff --git a/account/src/test/java/org/killbill/billing/account/api/user/TestEventJson.java b/account/src/test/java/org/killbill/billing/account/api/user/TestEventJson.java
index ed130b8..26d49dc 100644
--- a/account/src/test/java/org/killbill/billing/account/api/user/TestEventJson.java
+++ b/account/src/test/java/org/killbill/billing/account/api/user/TestEventJson.java
@@ -37,8 +37,8 @@ public class TestEventJson extends AccountTestSuiteNoDB {
@Test(groups = "fast", description="Test Account event deserialization")
public void testDefaultAccountChangeEvent() throws Exception {
final List<ChangedField> changes = new ArrayList<ChangedField>();
- changes.add(new DefaultChangedField("fieldXX", "valueX", "valueXXX"));
- changes.add(new DefaultChangedField("fieldYY", "valueY", "valueYYY"));
+ changes.add(new DefaultChangedField("fieldXX", "valueX", "valueXXX", clock.getUTCNow()));
+ changes.add(new DefaultChangedField("fieldYY", "valueY", "valueYYY", clock.getUTCNow()));
final AccountChangeInternalEvent e = new DefaultAccountChangeEvent(changes, UUID.randomUUID(), 1L, 2L, null);
final String json = mapper.writeValueAsString(e);
diff --git a/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java b/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java
index 67bbc28..9d097c4 100644
--- a/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java
+++ b/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -16,13 +18,12 @@
package org.killbill.billing.account.dao;
+import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
-import org.testng.Assert;
-
import org.killbill.billing.BillingExceptionBase;
import org.killbill.billing.ErrorCode;
import org.killbill.billing.account.api.Account;
@@ -33,8 +34,6 @@ import org.killbill.billing.account.api.DefaultMutableAccountData;
import org.killbill.billing.account.api.user.DefaultAccountChangeEvent;
import org.killbill.billing.account.api.user.DefaultAccountCreationEvent;
import org.killbill.billing.account.api.user.DefaultAccountCreationEvent.DefaultAccountData;
-import org.killbill.bus.api.PersistentBus;
-import org.killbill.bus.api.PersistentBus.EventBusException;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.events.AccountChangeInternalEvent;
@@ -42,6 +41,10 @@ import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.entity.DefaultPagination;
import org.killbill.billing.util.entity.Pagination;
import org.killbill.billing.util.entity.dao.MockEntityDaoBase;
+import org.killbill.bus.api.PersistentBus;
+import org.killbill.bus.api.PersistentBus.EventBusException;
+import org.killbill.clock.Clock;
+import org.testng.Assert;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
@@ -53,10 +56,12 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account,
private final MockEntityDaoBase<AccountModelDao, Account, AccountApiException> accountSqlDao = new MockEntityDaoBase<AccountModelDao, Account, AccountApiException>();
private final MockEntityDaoBase<AccountEmailModelDao, AccountEmail, AccountApiException> accountEmailSqlDao = new MockEntityDaoBase<AccountEmailModelDao, AccountEmail, AccountApiException>();
private final PersistentBus eventBus;
+ private final Clock clock;
@Inject
- public MockAccountDao(final PersistentBus eventBus) {
+ public MockAccountDao(final PersistentBus eventBus, final Clock clock) {
this.eventBus = eventBus;
+ this.clock = clock;
}
@Override
@@ -82,8 +87,8 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account,
final long tenantRecordId = context == null ? InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID
: context.getTenantRecordId();
final AccountChangeInternalEvent changeEvent = new DefaultAccountChangeEvent(account.getId(), currentAccount, account,
- accountRecordId, tenantRecordId, UUID.randomUUID()
- );
+ accountRecordId, tenantRecordId, UUID.randomUUID(),
+ clock.getUTCNow());
if (changeEvent.hasChanges()) {
try {
eventBus.post(changeEvent);
@@ -107,7 +112,7 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account,
@Override
public Pagination<AccountModelDao> searchAccounts(final String searchKey, final Long offset, final Long limit, final InternalTenantContext context) {
- final List<AccountModelDao> results = new LinkedList<AccountModelDao>();
+ final Collection<AccountModelDao> results = new LinkedList<AccountModelDao>();
int maxNbRecords = 0;
for (final AccountModelDao account : getAll(context)) {
maxNbRecords++;
@@ -146,7 +151,7 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account,
public void addEmail(final AccountEmailModelDao email, final InternalCallContext context) {
try {
accountEmailSqlDao.create(email, context);
- } catch (BillingExceptionBase billingExceptionBase) {
+ } catch (final BillingExceptionBase billingExceptionBase) {
Assert.fail(billingExceptionBase.toString());
}
}
diff --git a/account/src/test/java/org/killbill/billing/account/dao/TestAccountDao.java b/account/src/test/java/org/killbill/billing/account/dao/TestAccountDao.java
index 1ebcd3d..70fbbba 100644
--- a/account/src/test/java/org/killbill/billing/account/dao/TestAccountDao.java
+++ b/account/src/test/java/org/killbill/billing/account/dao/TestAccountDao.java
@@ -33,8 +33,6 @@ import org.killbill.billing.account.api.DefaultAccount;
import org.killbill.billing.account.api.DefaultAccountEmail;
import org.killbill.billing.account.api.DefaultMutableAccountData;
import org.killbill.billing.account.api.MutableAccountData;
-import org.killbill.billing.callcontext.InternalCallContext;
-import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.mock.MockAccountBuilder;
import org.killbill.billing.util.api.AuditLevel;
import org.killbill.billing.util.api.CustomFieldApiException;
@@ -112,29 +110,28 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
// Special test to verify audits - they are handled a bit differently due to the account record id (see EntitySqlDaoWrapperInvocationHandler#insertAudits)
final AccountModelDao account1 = createTestAccount();
accountDao.create(account1, internalCallContext);
- final Long account1RecordId = nonEntityDao.retrieveAccountRecordIdFromObject(account1.getId(), ObjectType.ACCOUNT, null);
- final InternalCallContext internalCallContext1 = new InternalCallContext(internalCallContext, account1RecordId);
+ refreshCallContext(account1.getId());
// Verify audits via account record id
- final DefaultAccountAuditLogs auditLogsForAccount1ViaAccountRecordId1 = auditDao.getAuditLogsForAccountRecordId(AuditLevel.FULL, internalCallContext1);
+ final DefaultAccountAuditLogs auditLogsForAccount1ViaAccountRecordId1 = auditDao.getAuditLogsForAccountRecordId(AuditLevel.FULL, internalCallContext);
Assert.assertEquals(auditLogsForAccount1ViaAccountRecordId1.getAuditLogsForAccount().size(), 1);
Assert.assertEquals(auditLogsForAccount1ViaAccountRecordId1.getAuditLogsForAccount().get(0).getChangeType(), ChangeType.INSERT);
// Add an entry in the account_history table to make sure we pick up the right
// record id / target record id / account record id in the audit_log table
- accountDao.updatePaymentMethod(account1.getId(), UUID.randomUUID(), internalCallContext1);
+ accountDao.updatePaymentMethod(account1.getId(), UUID.randomUUID(), internalCallContext);
final AccountModelDao account2 = createTestAccount();
accountDao.create(account2, internalCallContext);
- final Long account2RecordId = nonEntityDao.retrieveAccountRecordIdFromObject(account2.getId(), ObjectType.ACCOUNT, null);
- final InternalTenantContext internalTenantContext2 = new InternalCallContext(internalCallContext, account2RecordId);
+ refreshCallContext(account2.getId());
// Verify audits via account record id
- final DefaultAccountAuditLogs auditLogsForAccount2ViaAccountRecordId = auditDao.getAuditLogsForAccountRecordId(AuditLevel.FULL, internalTenantContext2);
+ final DefaultAccountAuditLogs auditLogsForAccount2ViaAccountRecordId = auditDao.getAuditLogsForAccountRecordId(AuditLevel.FULL, internalCallContext);
Assert.assertEquals(auditLogsForAccount2ViaAccountRecordId.getAuditLogsForAccount().size(), 1);
Assert.assertEquals(auditLogsForAccount2ViaAccountRecordId.getAuditLogsForAccount().get(0).getChangeType(), ChangeType.INSERT);
- final DefaultAccountAuditLogs auditLogsForAccount1ViaAccountRecordId2 = auditDao.getAuditLogsForAccountRecordId(AuditLevel.FULL, internalCallContext1);
+ refreshCallContext(account1.getId());
+ final DefaultAccountAuditLogs auditLogsForAccount1ViaAccountRecordId2 = auditDao.getAuditLogsForAccountRecordId(AuditLevel.FULL, internalCallContext);
Assert.assertEquals(auditLogsForAccount1ViaAccountRecordId2.getAuditLogsForAccount().size(), 2);
Assert.assertEquals(auditLogsForAccount1ViaAccountRecordId2.getAuditLogsForAccount().get(0).getChangeType(), ChangeType.INSERT);
Assert.assertEquals(auditLogsForAccount1ViaAccountRecordId2.getAuditLogsForAccount().get(1).getChangeType(), ChangeType.UPDATE);
diff --git a/api/src/main/java/org/killbill/billing/account/api/AccountInternalApi.java b/api/src/main/java/org/killbill/billing/account/api/AccountInternalApi.java
index 4f7a92f..ad70272 100644
--- a/api/src/main/java/org/killbill/billing/account/api/AccountInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/account/api/AccountInternalApi.java
@@ -1,7 +1,9 @@
/*
- * Copyright 2010-2011 Ning, Inc.
+ * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -22,7 +24,7 @@ import java.util.UUID;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
-public interface AccountInternalApi {
+public interface AccountInternalApi extends ImmutableAccountInternalApi {
Account getAccountByKey(String key, InternalTenantContext context) throws AccountApiException;
@@ -41,9 +43,4 @@ public interface AccountInternalApi {
void updatePaymentMethod(UUID accountId, UUID paymentMethodId, InternalCallContext context) throws AccountApiException;
UUID getByRecordId(Long recordId, InternalTenantContext context) throws AccountApiException;
-
- ImmutableAccountData getImmutableAccountDataById(UUID accountId, InternalTenantContext context) throws AccountApiException;
-
- ImmutableAccountData getImmutableAccountDataByRecordId(Long recordId, InternalTenantContext context) throws AccountApiException;
-
}
diff --git a/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java b/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java
index f1429a0..bc78b08 100644
--- a/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java
+++ b/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java
@@ -1,7 +1,9 @@
/*
- * Copyright 2010-2012 Ning, Inc.
+ * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -21,7 +23,6 @@ import java.util.UUID;
import javax.annotation.Nullable;
import org.joda.time.DateTime;
-
import org.joda.time.DateTimeZone;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.CallOrigin;
@@ -42,10 +43,19 @@ public class InternalCallContext extends InternalTenantContext {
private final DateTime createdDate;
private final DateTime updatedDate;
- public InternalCallContext(final Long tenantRecordId, @Nullable final Long accountRecordId, final UUID userToken, final String userName,
- final CallOrigin callOrigin, final UserType userType, final String reasonCode, final String comment,
- final DateTime createdDate, final DateTime updatedDate) {
- super(tenantRecordId, accountRecordId);
+ public InternalCallContext(final Long tenantRecordId,
+ @Nullable final Long accountRecordId,
+ @Nullable final DateTimeZone fixedOffsetTimeZone,
+ @Nullable final DateTime referenceDateTime,
+ final UUID userToken,
+ final String userName,
+ final CallOrigin callOrigin,
+ final UserType userType,
+ final String reasonCode,
+ final String comment,
+ final DateTime createdDate,
+ final DateTime updatedDate) {
+ super(tenantRecordId, accountRecordId, fixedOffsetTimeZone, referenceDateTime);
this.userToken = userToken;
this.createdBy = userName;
this.updatedBy = userName;
@@ -53,26 +63,38 @@ public class InternalCallContext extends InternalTenantContext {
this.contextUserType = userType;
this.reasonCode = reasonCode;
this.comments = comment;
- this.createdDate = new DateTime(createdDate, DateTimeZone.UTC);
+ this.createdDate = createdDate;
this.updatedDate = updatedDate;
}
- public InternalCallContext(final Long tenantRecordId, @Nullable final Long accountRecordId, final CallContext callContext) {
- this(tenantRecordId, accountRecordId, callContext.getUserToken(), callContext.getUserName(), callContext.getCallOrigin(),
- callContext.getUserType(), callContext.getReasonCode(), callContext.getComments(), callContext.getCreatedDate(),
- callContext.getUpdatedDate());
- }
-
- public InternalCallContext(final InternalCallContext context, final Long accountRecordId) {
- this(context.getTenantRecordId(), accountRecordId, context.getUserToken(), context.getCreatedBy(), context.getCallOrigin(),
- context.getContextUserType(), context.getReasonCode(), context.getComments(), context.getCreatedDate(),
- context.getUpdatedDate());
- }
-
- public InternalCallContext(final InternalCallContext context, final Long accountRecordId, final Long tenantRecordId) {
- this(tenantRecordId, accountRecordId, context.getUserToken(), context.getCreatedBy(), context.getCallOrigin(),
- context.getContextUserType(), context.getReasonCode(), context.getComments(), context.getCreatedDate(),
- context.getUpdatedDate());
+ public InternalCallContext(final Long tenantRecordId, final CallContext callContext, final DateTime utcNow) {
+ this(tenantRecordId,
+ null,
+ null,
+ null,
+ callContext.getUserToken(),
+ callContext.getUserName(),
+ callContext.getCallOrigin(),
+ callContext.getUserType(),
+ callContext.getReasonCode(),
+ callContext.getComments(),
+ utcNow,
+ utcNow);
+ }
+
+ public InternalCallContext(final InternalCallContext context, final Long accountRecordId, final DateTimeZone fixedOffsetTimeZone, final DateTime referenceDateTime, final DateTime utcNow) {
+ this(context.getTenantRecordId(),
+ accountRecordId,
+ fixedOffsetTimeZone,
+ referenceDateTime,
+ context.getUserToken(),
+ context.getCreatedBy(),
+ context.getCallOrigin(),
+ context.getContextUserType(),
+ context.getReasonCode(),
+ context.getComments(),
+ utcNow,
+ utcNow);
}
// TODO should not be needed if all services are using internal API
diff --git a/api/src/main/java/org/killbill/billing/callcontext/InternalTenantContext.java b/api/src/main/java/org/killbill/billing/callcontext/InternalTenantContext.java
index f35241d..8880c72 100644
--- a/api/src/main/java/org/killbill/billing/callcontext/InternalTenantContext.java
+++ b/api/src/main/java/org/killbill/billing/callcontext/InternalTenantContext.java
@@ -1,7 +1,9 @@
/*
- * Copyright 2010-2012 Ning, Inc.
+ * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -20,23 +22,29 @@ import java.util.UUID;
import javax.annotation.Nullable;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
import org.killbill.billing.util.callcontext.TenantContext;
/**
* Internal use only
*/
-public class InternalTenantContext {
+public class InternalTenantContext extends TimeAwareContext {
protected final Long tenantRecordId;
protected final Long accountRecordId;
- public InternalTenantContext(final Long tenantRecordId, @Nullable final Long accountRecordId) {
+ public InternalTenantContext(final Long tenantRecordId,
+ @Nullable final Long accountRecordId,
+ @Nullable final DateTimeZone fixedOffsetTimeZone,
+ @Nullable final DateTime referenceDateTime) {
+ super(fixedOffsetTimeZone, referenceDateTime);
this.tenantRecordId = tenantRecordId;
this.accountRecordId = accountRecordId;
}
- public InternalTenantContext(final long defaultTenantRecordId) {
- this(defaultTenantRecordId, null);
+ public InternalTenantContext(final Long defaultTenantRecordId) {
+ this(defaultTenantRecordId, null, null, null);
}
public TenantContext toTenantContext(final UUID tenantId) {
@@ -55,8 +63,8 @@ public class InternalTenantContext {
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("InternalTenantContext");
- sb.append("{accountRecordId=").append(accountRecordId);
- sb.append(", tenantRecordId=").append(tenantRecordId);
+ sb.append("{accountRecordId=").append(getAccountRecordId());
+ sb.append(", tenantRecordId=").append(getTenantRecordId());
sb.append('}');
return sb.toString();
}
@@ -72,10 +80,10 @@ public class InternalTenantContext {
final InternalTenantContext that = (InternalTenantContext) o;
- if (accountRecordId != null ? !accountRecordId.equals(that.accountRecordId) : that.accountRecordId != null) {
+ if (getAccountRecordId() != null ? !getAccountRecordId().equals(that.getAccountRecordId()) : that.getAccountRecordId() != null) {
return false;
}
- if (tenantRecordId != null ? !tenantRecordId.equals(that.tenantRecordId) : that.tenantRecordId != null) {
+ if (getTenantRecordId() != null ? !getTenantRecordId().equals(that.getTenantRecordId()) : that.getTenantRecordId() != null) {
return false;
}
@@ -84,8 +92,8 @@ public class InternalTenantContext {
@Override
public int hashCode() {
- int result = accountRecordId != null ? accountRecordId.hashCode() : 0;
- result = 31 * result + (tenantRecordId != null ? tenantRecordId.hashCode() : 0);
+ int result = getAccountRecordId() != null ? getAccountRecordId().hashCode() : 0;
+ result = 31 * result + (getTenantRecordId() != null ? getTenantRecordId().hashCode() : 0);
return result;
}
}
diff --git a/api/src/main/java/org/killbill/billing/callcontext/TimeAwareContext.java b/api/src/main/java/org/killbill/billing/callcontext/TimeAwareContext.java
new file mode 100644
index 0000000..89f710c
--- /dev/null
+++ b/api/src/main/java/org/killbill/billing/callcontext/TimeAwareContext.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2016 Groupon, Inc
+ * Copyright 2016 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.callcontext;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+import org.joda.time.LocalTime;
+import org.killbill.clock.ClockUtil;
+
+public class TimeAwareContext {
+
+ private final DateTimeZone fixedOffsetTimeZone;
+ private final LocalTime referenceTime;
+
+ public TimeAwareContext(@Nullable final DateTimeZone fixedOffsetTimeZone, @Nullable final DateTime referenceDateTime) {
+ this.fixedOffsetTimeZone = fixedOffsetTimeZone;
+ this.referenceTime = computeReferenceTime(referenceDateTime);
+ }
+
+ public DateTime toUTCDateTime(final LocalDate localDate) {
+ validateContext();
+
+ return ClockUtil.toUTCDateTime(localDate, getReferenceTime(), getFixedOffsetTimeZone());
+ }
+
+ public LocalDate toLocalDate(final DateTime dateTime) {
+ validateContext();
+
+ return ClockUtil.toLocalDate(dateTime, getFixedOffsetTimeZone());
+ }
+
+ private void validateContext() {
+ if (getFixedOffsetTimeZone() == null || getReferenceTime() == null) {
+ throw new IllegalArgumentException(String.format("Context mis-configured: fixedOffsetTimeZone=%s, referenceTime=%s", getFixedOffsetTimeZone(), getReferenceTime()));
+ }
+ }
+
+ // For convenience (used in tests)
+
+ //@VisibleForTesting
+ protected LocalTime computeReferenceTime(@Nullable final DateTime referenceTime) {
+ return referenceTime == null ? null : ClockUtil.toDateTime(referenceTime, getFixedOffsetTimeZone()).toLocalTime();
+ }
+
+ //@VisibleForTesting
+ public DateTimeZone getFixedOffsetTimeZone() {
+ return fixedOffsetTimeZone;
+ }
+
+ //@VisibleForTesting
+ public LocalTime getReferenceTime() {
+ return referenceTime;
+ }
+}
diff --git a/api/src/main/java/org/killbill/billing/entitlement/EntitlementInternalApi.java b/api/src/main/java/org/killbill/billing/entitlement/EntitlementInternalApi.java
index f2eef61..c524651 100644
--- a/api/src/main/java/org/killbill/billing/entitlement/EntitlementInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/entitlement/EntitlementInternalApi.java
@@ -30,7 +30,7 @@ import org.killbill.billing.payment.api.PluginProperty;
public interface EntitlementInternalApi {
- AccountEntitlements getAllEntitlementsForAccountId(UUID accountId, InternalTenantContext context) throws EntitlementApiException;
+ AccountEntitlements getAllEntitlementsForAccount(InternalTenantContext context) throws EntitlementApiException;
Entitlement getEntitlementForId(final UUID uuid, final InternalTenantContext tenantContext) throws EntitlementApiException;
diff --git a/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java b/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java
index b2e6d4b..188e714 100644
--- a/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java
+++ b/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java
@@ -42,6 +42,8 @@ public interface EventsStream {
EntitlementState getEntitlementState();
+ LocalDate getEntitlementEffectiveStartDate();
+
LocalDate getEntitlementEffectiveEndDate();
SubscriptionBase getSubscriptionBase();
diff --git a/api/src/main/java/org/killbill/billing/events/BlockingTransitionInternalEvent.java b/api/src/main/java/org/killbill/billing/events/BlockingTransitionInternalEvent.java
index b31dbdd..010901d 100644
--- a/api/src/main/java/org/killbill/billing/events/BlockingTransitionInternalEvent.java
+++ b/api/src/main/java/org/killbill/billing/events/BlockingTransitionInternalEvent.java
@@ -18,6 +18,7 @@ package org.killbill.billing.events;
import java.util.UUID;
+import org.joda.time.DateTime;
import org.killbill.billing.entitlement.api.BlockingStateType;
// Event for effective blocking state changes (not entitlement specific)
@@ -27,6 +28,12 @@ public interface BlockingTransitionInternalEvent extends BusInternalEvent {
public BlockingStateType getBlockingType();
+ public String getStateName();
+
+ public String getService();
+
+ public DateTime getEffectiveDate();
+
public Boolean isTransitionedToBlockedBilling();
public Boolean isTransitionedToUnblockedBilling();
diff --git a/api/src/main/java/org/killbill/billing/glue/InvoiceModule.java b/api/src/main/java/org/killbill/billing/glue/InvoiceModule.java
index e782366..ca13341 100644
--- a/api/src/main/java/org/killbill/billing/glue/InvoiceModule.java
+++ b/api/src/main/java/org/killbill/billing/glue/InvoiceModule.java
@@ -22,7 +22,5 @@ public interface InvoiceModule {
public void installInvoicePaymentApi();
- public void installInvoiceMigrationApi();
-
public void installInvoiceInternalApi();
}
diff --git a/api/src/main/java/org/killbill/billing/glue/SubscriptionModule.java b/api/src/main/java/org/killbill/billing/glue/SubscriptionModule.java
index d561355..b970560 100644
--- a/api/src/main/java/org/killbill/billing/glue/SubscriptionModule.java
+++ b/api/src/main/java/org/killbill/billing/glue/SubscriptionModule.java
@@ -22,8 +22,6 @@ public interface SubscriptionModule {
public void installSubscriptionTransferApi();
- public void installSubscriptionMigrationApi();
-
public void installSubscriptionInternalApi();
public void installSubscriptionTimelineApi();
diff --git a/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java b/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java
index 7c930cd..4899368 100644
--- a/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java
@@ -43,8 +43,6 @@ public interface InvoiceInternalApi {
public InvoicePayment getInvoicePaymentForAttempt(UUID paymentId, InternalTenantContext context) throws InvoiceApiException;
- public InvoicePayment getInvoicePaymentForRefund(UUID paymentId, InternalTenantContext context) throws InvoiceApiException;
-
public InvoicePayment getInvoicePaymentForChargeback(UUID paymentId, InternalTenantContext context) throws InvoiceApiException;
public Invoice getInvoiceForPaymentId(UUID paymentId, InternalTenantContext context) throws InvoiceApiException;
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 6e1828d..b44faa9 100644
--- a/api/src/main/java/org/killbill/billing/junction/BillingEventSet.java
+++ b/api/src/main/java/org/killbill/billing/junction/BillingEventSet.java
@@ -1,7 +1,9 @@
/*
- * Copyright 2010-2011 Ning, Inc.
+ * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -23,7 +25,6 @@ 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> {
@@ -33,7 +34,5 @@ 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/DefaultBlockingState.java b/api/src/main/java/org/killbill/billing/junction/DefaultBlockingState.java
index 07537b0..b44b81d 100644
--- a/api/src/main/java/org/killbill/billing/junction/DefaultBlockingState.java
+++ b/api/src/main/java/org/killbill/billing/junction/DefaultBlockingState.java
@@ -204,6 +204,7 @@ public class DefaultBlockingState extends EntityBase implements BlockingState {
return true;
}
+
@Override
public int hashCode() {
int result = super.hashCode();
diff --git a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBase.java b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBase.java
index 02d6c5b..07bec90 100644
--- a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBase.java
+++ b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBase.java
@@ -96,6 +96,8 @@ public interface SubscriptionBase extends Entity, Blockable {
public DateTime getChargedThroughDate();
+ public boolean isMigrated();
+
public ProductCategory getCategory();
public SubscriptionBaseTransition getPendingTransition();
diff --git a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java
index df97745..a8ccdf6 100644
--- a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java
@@ -43,10 +43,10 @@ public interface SubscriptionBaseInternalApi {
public SubscriptionBase createSubscription(UUID bundleId, PlanPhaseSpecifier spec, List<PlanPhasePriceOverride> overrides, DateTime requestedDateWithMs,
- InternalCallContext context) throws SubscriptionBaseApiException;
+ final boolean isMigrated, InternalCallContext context) throws SubscriptionBaseApiException;
- public SubscriptionBase createBaseSubscriptionWithAddOns(UUID bundleId, Iterable<EntitlementSpecifier> entitlements, DateTime requestedDateWithMs,
- InternalCallContext context) throws SubscriptionBaseApiException;
+ public List<SubscriptionBase> createBaseSubscriptionWithAddOns(UUID bundleId, Iterable<EntitlementSpecifier> entitlements, DateTime requestedDateWithMs,
+ final boolean isMigrated, InternalCallContext context) throws SubscriptionBaseApiException;
public void cancelBaseSubscriptions(Iterable<SubscriptionBase> subscriptions, BillingActionPolicy policy, InternalCallContext context) throws SubscriptionBaseApiException;
diff --git a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseTransitionType.java b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseTransitionType.java
index 11318ce..7a7729e 100644
--- a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseTransitionType.java
+++ b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseTransitionType.java
@@ -21,18 +21,10 @@ package org.killbill.billing.subscription.api;
*/
public enum SubscriptionBaseTransitionType {
/**
- * Occurs when a {@code SubscriptionBase} got migrated to mark the start of the subscription
- */
- MIGRATE_ENTITLEMENT,
- /**
* Occurs when a a user created a {@code SubscriptionBase} (not migrated)
*/
CREATE,
/**
- * Occurs when a {@code SubscriptionBase} got migrated to mark the start of the billing
- */
- MIGRATE_BILLING,
- /**
* Occurs when a {@code SubscriptionBase} got transferred to mark the start of the subscription
*/
TRANSFER,
@@ -41,10 +33,6 @@ public enum SubscriptionBaseTransitionType {
*/
CHANGE,
/**
- * Occurs when a user restarted a {@code SubscriptionBase} after it had been cancelled
- */
- RE_CREATE,
- /**
* Occurs when a user cancelled the {@code SubscriptionBase}
*/
CANCEL,
diff --git a/api/src/main/java/org/killbill/billing/subscription/api/timeline/SubscriptionBaseTimeline.java b/api/src/main/java/org/killbill/billing/subscription/api/timeline/SubscriptionBaseTimeline.java
index a0a981f..dc56c4e 100644
--- a/api/src/main/java/org/killbill/billing/subscription/api/timeline/SubscriptionBaseTimeline.java
+++ b/api/src/main/java/org/killbill/billing/subscription/api/timeline/SubscriptionBaseTimeline.java
@@ -39,12 +39,6 @@ public interface SubscriptionBaseTimeline extends Entity {
*/
public List<ExistingEvent> getExistingEvents();
- /**
- * @return the active version for the event stream
- */
- public long getActiveVersion();
-
-
public interface ExistingEvent {
@@ -59,10 +53,6 @@ public interface SubscriptionBaseTimeline extends Entity {
*/
public PlanPhaseSpecifier getPlanPhaseSpecifier();
- /**
- * @return the date at which this event should be inserted into the stream
- */
- public DateTime getRequestedDate();
/**
* @return the {@code SubscriptionBaseTransitionType} for the event
beatrix/pom.xml 6(+6 -0)
diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index 5be9267..2932b8a 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -267,6 +267,12 @@
</dependency>
<dependency>
<groupId>org.kill-bill.commons</groupId>
+ <artifactId>killbill-queue</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.kill-bill.commons</groupId>
<artifactId>killbill-xmlloader</artifactId>
</dependency>
<dependency>
diff --git a/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java b/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java
index 16b5b66..3b88c15 100644
--- a/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java
+++ b/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java
@@ -26,8 +26,9 @@ import javax.inject.Named;
import org.killbill.billing.ObjectType;
import org.killbill.billing.callcontext.InternalCallContext;
-import org.killbill.billing.entitlement.EntitlementTransitionType;
+import org.killbill.billing.entitlement.EntitlementService;
import org.killbill.billing.entitlement.api.BlockingStateType;
+import org.killbill.billing.entitlement.api.DefaultEntitlementApi;
import org.killbill.billing.events.AccountChangeInternalEvent;
import org.killbill.billing.events.AccountCreationInternalEvent;
import org.killbill.billing.events.BlockingTransitionInternalEvent;
@@ -38,10 +39,11 @@ import org.killbill.billing.events.ControlTagCreationInternalEvent;
import org.killbill.billing.events.ControlTagDeletionInternalEvent;
import org.killbill.billing.events.CustomFieldCreationEvent;
import org.killbill.billing.events.CustomFieldDeletionEvent;
-import org.killbill.billing.events.EntitlementInternalEvent;
import org.killbill.billing.events.InvoiceAdjustmentInternalEvent;
import org.killbill.billing.events.InvoiceCreationInternalEvent;
import org.killbill.billing.events.InvoiceNotificationInternalEvent;
+import org.killbill.billing.events.InvoicePaymentErrorInternalEvent;
+import org.killbill.billing.events.InvoicePaymentInfoInternalEvent;
import org.killbill.billing.events.OverdueChangeInternalEvent;
import org.killbill.billing.events.PaymentErrorInternalEvent;
import org.killbill.billing.events.PaymentInfoInternalEvent;
@@ -52,6 +54,7 @@ import org.killbill.billing.events.TenantConfigDeletionInternalEvent;
import org.killbill.billing.events.UserTagCreationInternalEvent;
import org.killbill.billing.events.UserTagDeletionInternalEvent;
import org.killbill.billing.lifecycle.glue.BusModule;
+import org.killbill.billing.notification.plugin.api.BlockingStateMetadata;
import org.killbill.billing.notification.plugin.api.BroadcastMetadata;
import org.killbill.billing.notification.plugin.api.ExtBusEventType;
import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
@@ -101,7 +104,13 @@ public class BeatrixListener {
externalBus.post(externalEvent);
}
} catch (final EventBusException e) {
+ //
+ // When using PersistentBus this should never be reached, because errors are caught at the 'ext' bus level and retried until either success or event row is moved to FAILED status.
+ // However when using InMemoryBus, this can happen as there is no retry logic (at the 'ext' bus level) and so we should re-throw at this level to kick-in the retry logic from the 'main' bus
+ // (The use of RuntimeException is somewhat arbitrary)
+ //
log.warn("Failed to dispatch external bus events", e);
+ throw new RuntimeException(e);
} catch (JsonProcessingException e) {
log.warn("Failed to dispatch external bus events", e);
}
@@ -134,9 +143,7 @@ public class BeatrixListener {
objectType = ObjectType.SUBSCRIPTION;
objectId = realEventST.getSubscriptionId();
if (realEventST.getTransitionType() == SubscriptionBaseTransitionType.CREATE ||
- realEventST.getTransitionType() == SubscriptionBaseTransitionType.RE_CREATE ||
- realEventST.getTransitionType() == SubscriptionBaseTransitionType.TRANSFER ||
- realEventST.getTransitionType() == SubscriptionBaseTransitionType.MIGRATE_ENTITLEMENT) {
+ realEventST.getTransitionType() == SubscriptionBaseTransitionType.TRANSFER) {
eventBusType = ExtBusEventType.SUBSCRIPTION_CREATION;
} else if (realEventST.getTransitionType() == SubscriptionBaseTransitionType.CANCEL) {
eventBusType = ExtBusEventType.SUBSCRIPTION_CANCEL;
@@ -159,17 +166,24 @@ public class BeatrixListener {
objectType = ObjectType.SUBSCRIPTION;
}
objectId = realEventBS.getBlockableId();
- // Probably we should serialize the isTransitionedTo* from BlockingTransitionInternalEvent into the metdata section
- break;
- case ENTITLEMENT_TRANSITION:
- final EntitlementInternalEvent realEventET = (EntitlementInternalEvent) event;
- objectType = ObjectType.BUNDLE;
- objectId = realEventET.getBundleId();
- if (realEventET.getTransitionType() == EntitlementTransitionType.BLOCK_BUNDLE) {
- eventBusType = ExtBusEventType.BUNDLE_PAUSE;
- } else if (realEventET.getTransitionType() == EntitlementTransitionType.UNBLOCK_BUNDLE) {
- eventBusType = ExtBusEventType.BUNDLE_RESUME;
+ if (EntitlementService.ENTITLEMENT_SERVICE_NAME.equals(realEventBS.getService())) {
+ if (DefaultEntitlementApi.ENT_STATE_START.equals(realEventBS.getStateName())) {
+ eventBusType = ExtBusEventType.ENTITLEMENT_CREATION;
+ } else if (DefaultEntitlementApi.ENT_STATE_BLOCKED.equals(realEventBS.getStateName())) {
+ eventBusType = ExtBusEventType.BUNDLE_PAUSE;
+ } else if (DefaultEntitlementApi.ENT_STATE_CLEAR.equals(realEventBS.getStateName())) {
+ eventBusType = ExtBusEventType.BUNDLE_RESUME;
+ } else if (DefaultEntitlementApi.ENT_STATE_CANCELLED.equals(realEventBS.getStateName())) {
+ eventBusType = ExtBusEventType.ENTITLEMENT_CANCEL;
+ }
+ } else {
+ eventBusType = ExtBusEventType.BLOCKING_STATE;
+
+ final BlockingStateMetadata metaDataObj = new BlockingStateMetadata(realEventBS.getBlockableId(), realEventBS.getService(), realEventBS.getStateName(), realEventBS.getBlockingType(), realEventBS.getEffectiveDate(),
+ realEventBS.isTransitionedToBlockedBilling(), realEventBS.isTransitionedToUnblockedBilling(),
+ realEventBS.isTransitionedToBlockedEntitlement(), realEventBS.isTransitionedToUnblockedEntitlement());
+ metaData = objectMapper.writeValueAsString(metaDataObj);
}
break;
@@ -196,6 +210,20 @@ public class BeatrixListener {
eventBusType = ExtBusEventType.INVOICE_ADJUSTMENT;
break;
+ case INVOICE_PAYMENT_INFO:
+ final InvoicePaymentInfoInternalEvent realEventInvPay = (InvoicePaymentInfoInternalEvent) event;
+ objectType = ObjectType.INVOICE;
+ objectId = realEventInvPay.getInvoiceId();
+ eventBusType = ExtBusEventType.INVOICE_PAYMENT_SUCCESS;
+ break;
+
+ case INVOICE_PAYMENT_ERROR:
+ final InvoicePaymentErrorInternalEvent realEventInvPayErr = (InvoicePaymentErrorInternalEvent) event;
+ objectType = ObjectType.INVOICE;
+ objectId = realEventInvPayErr.getInvoiceId();
+ eventBusType = ExtBusEventType.INVOICE_PAYMENT_FAILED;
+ break;
+
case PAYMENT_INFO:
final PaymentInfoInternalEvent realEventPay = (PaymentInfoInternalEvent) event;
objectType = ObjectType.PAYMENT;
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/extbus/TestEventJson.java b/beatrix/src/test/java/org/killbill/billing/beatrix/extbus/TestEventJson.java
index 8238496..77ab028 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/extbus/TestEventJson.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/extbus/TestEventJson.java
@@ -24,10 +24,10 @@ import org.testng.Assert;
import org.testng.annotations.Test;
import org.killbill.billing.ObjectType;
-import org.killbill.billing.beatrix.BeatrixTestSuite;
+import org.killbill.billing.beatrix.BeatrixTestSuiteNoDB;
import org.killbill.billing.util.jackson.ObjectMapper;
-public class TestEventJson extends BeatrixTestSuite {
+public class TestEventJson extends BeatrixTestSuiteNoDB {
private final ObjectMapper mapper = new ObjectMapper();
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestBillingAlignment.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestBillingAlignment.java
index 9ee5303..de9b23d 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestBillingAlignment.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestBillingAlignment.java
@@ -39,18 +39,18 @@ public class TestBillingAlignment extends TestIntegrationBase {
@Test(groups = "slow")
public void testTransitionAccountBAToSubscriptionBA() throws Exception {
- // Set the BCD to the 25th
- final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(25));
-
// We take april as it has 30 days (easier to play with BCD)
// Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
clock.setDay(new LocalDate(2012, 4, 1));
+ // Set the BCD to the 25th
+ final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(25));
+
//
// CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
// (Start with monthly that has an 'Account' billing alignment)
//
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueBase.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueBase.java
index 5f5b4e2..29a302c 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueBase.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueBase.java
@@ -63,13 +63,16 @@ public abstract class TestOverdueBase extends TestIntegrationBase {
final DefaultOverdueConfig config = XMLLoader.getObjectFromStreamNoValidation(is, DefaultOverdueConfig.class);
overdueConfigCache.loadDefaultOverdueConfig(config);
+ productName = "Shotgun";
+ term = BillingPeriod.MONTHLY;
+ paymentPlugin.clear();
+ }
+
+ protected void setupAccount() throws Exception {
account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
assertNotNull(account);
paymentApi.addPaymentMethod(account, UUID.randomUUID().toString(), BeatrixIntegrationModule.NON_OSGI_PLUGIN_NAME, true, paymentMethodPlugin, PLUGIN_PROPERTIES, callContext);
- productName = "Shotgun";
- term = BillingPeriod.MONTHLY;
- paymentPlugin.clear();
}
protected void checkODState(final String expected) {
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java
index d0797dd..dbdd9c0 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java
@@ -121,9 +121,11 @@ public class TestOverdueIntegration extends TestOverdueBase {
// 2012-05-01T00:03:42.000Z
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+ setupAccount();
+
// Set next invoice to fail and create subscription
paymentPlugin.makeAllInvoicesFailWithError(true);
- final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -208,9 +210,11 @@ public class TestOverdueIntegration extends TestOverdueBase {
// 2012-05-01T00:03:42.000Z
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+ setupAccount();
+
// Set next invoice to fail and create subscription
paymentPlugin.makeAllInvoicesFailWithError(true);
- final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -302,9 +306,11 @@ public class TestOverdueIntegration extends TestOverdueBase {
// 2012-05-01T00:03:42.000Z
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+ setupAccount();
+
// Set next invoice to fail and create subscription
paymentPlugin.makeAllInvoicesFailWithError(true);
- final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -403,9 +409,11 @@ public class TestOverdueIntegration extends TestOverdueBase {
// 2012-05-01T00:03:42.000Z
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+ setupAccount();
+
// Set next invoice to fail and create subscription
paymentPlugin.makeAllInvoicesFailWithError(true);
- final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
@@ -491,9 +499,11 @@ public class TestOverdueIntegration extends TestOverdueBase {
// 2012-05-01T00:03:42.000Z
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+ setupAccount();
+
// Set next invoice to fail and create subscription
paymentPlugin.makeAllInvoicesFailWithError(true);
- final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -569,11 +579,13 @@ public class TestOverdueIntegration extends TestOverdueBase {
// 2012-05-01T00:03:42.000Z
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+ setupAccount();
+
// Make sure the account doesn't have any payment method
accountInternalApi.removePaymentMethod(account.getId(), internalCallContext);
// Create subscription
- final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -664,8 +676,10 @@ public class TestOverdueIntegration extends TestOverdueBase {
// 2012-05-01T00:03:42.000Z
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+ setupAccount();
+
// Create a subscription without failing payments
- final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -701,8 +715,10 @@ public class TestOverdueIntegration extends TestOverdueBase {
// 2012-05-01T00:03:42.000Z
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+ setupAccount();
+
// Create subscription and don't fail payments
- final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -733,7 +749,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
checkODState(OverdueWrapper.CLEAR_STATE_NAME);
// Now, refund the second (first non-zero dollar) invoice
- final Payment payment = paymentApi.getPayment(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).get(1).getPayments().get(0).getPaymentId(), false, PLUGIN_PROPERTIES, callContext);
+ final Payment payment = paymentApi.getPayment(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).get(1).getPayments().get(0).getPaymentId(), false, PLUGIN_PROPERTIES, callContext);
refundPaymentAndCheckForCompletion(account, payment, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.BLOCK);
// We should now be in OD1
checkODState("OD1");
@@ -745,8 +761,10 @@ public class TestOverdueIntegration extends TestOverdueBase {
// 2012-05-01T00:03:42.000Z
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+ setupAccount();
+
// Create subscription and don't fail payments
- final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -777,7 +795,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
checkODState(OverdueWrapper.CLEAR_STATE_NAME);
// Now, create a chargeback for the second (first non-zero dollar) invoice
- final InvoicePayment invoicePayment = invoicePaymentApi.getInvoicePayments(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).get(1).getPayments().get(0).getPaymentId(), callContext).get(0);
+ final InvoicePayment invoicePayment = invoicePaymentApi.getInvoicePayments(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).get(1).getPayments().get(0).getPaymentId(), callContext).get(0);
final Payment payment = paymentApi.getPayment(invoicePayment.getPaymentId(), false, ImmutableList.<PluginProperty>of(), callContext);
createChargeBackAndCheckForCompletion(account, payment, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.BLOCK);
// We should now be in OD1
@@ -790,9 +808,11 @@ public class TestOverdueIntegration extends TestOverdueBase {
// 2012-05-01T00:03:42.000Z
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+ setupAccount();
+
// Set next invoice to fail and create subscription
paymentPlugin.makeAllInvoicesFailWithError(true);
- final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -838,9 +858,11 @@ public class TestOverdueIntegration extends TestOverdueBase {
// 2012-05-01T00:03:42.000Z
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+ setupAccount();
+
// Set next invoice to fail and create subscription
paymentPlugin.makeAllInvoicesFailWithError(true);
- final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -939,9 +961,9 @@ public class TestOverdueIntegration extends TestOverdueBase {
createPaymentAndCheckForCompletion(account, invoice, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
} else {
if (extraPayment) {
- createPaymentAndCheckForCompletion(account, invoice, NextEvent.BLOCK, NextEvent.TAG, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ createPaymentAndCheckForCompletion(account, invoice, NextEvent.BLOCK, NextEvent.TAG, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
} else {
- createPaymentAndCheckForCompletion(account, invoice, NextEvent.BLOCK, NextEvent.TAG, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ createPaymentAndCheckForCompletion(account, invoice, NextEvent.BLOCK, NextEvent.TAG, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
}
}
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java
index 9575752..a2ea242 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java
@@ -30,7 +30,6 @@ import org.killbill.billing.entitlement.api.DefaultEntitlement;
import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
import org.killbill.billing.entitlement.api.SubscriptionBundle;
import org.killbill.billing.invoice.api.InvoiceItemType;
-import org.killbill.billing.junction.DefaultBlockingState;
import org.killbill.billing.overdue.wrapper.OverdueWrapper;
import org.killbill.billing.subscription.api.SubscriptionBase;
import org.testng.annotations.Test;
@@ -70,24 +69,26 @@ public class TestOverdueWithSubscriptionCancellation extends TestOverdueBase {
public void testCheckSubscriptionCancellation() throws Exception {
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+ setupAccount();
+
// Set next invoice to fail and create subscription
paymentPlugin.makeAllInvoicesFailWithError(true);
- final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
- final DefaultEntitlement addOn1 = addAOEntitlementAndCheckForCompletion(baseEntitlement.getBundleId(), "Holster", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement addOn1 = addAOEntitlementAndCheckForCompletion(baseEntitlement.getBundleId(), "Holster", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
- final DefaultEntitlement addOn2 = addAOEntitlementAndCheckForCompletion(baseEntitlement.getBundleId(), "Holster", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement addOn2 = addAOEntitlementAndCheckForCompletion(baseEntitlement.getBundleId(), "Holster", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
// Cancel addOn1 one day after
clock.addDays(1);
- cancelEntitlementAndCheckForCompletion(addOn1, clock.getUTCNow(), NextEvent.BLOCK, NextEvent.CANCEL);
+ cancelEntitlementAndCheckForCompletion(addOn1, NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.NULL_INVOICE);
// DAY 30 have to get out of trial before first payment
- addDaysAndCheckForCompletion(29, NextEvent.PHASE, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+ addDaysAndCheckForCompletion(29, NextEvent.PHASE, NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
@@ -116,31 +117,33 @@ public class TestOverdueWithSubscriptionCancellation extends TestOverdueBase {
// 2012-05-01T00:03:53.000Z
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+ setupAccount();
+
// Set next invoice to fail and create subscription
paymentPlugin.makeAllInvoicesFailWithError(true);
- final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
- final DefaultEntitlement baseEntitlement2 = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey2", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement2 = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey2", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
final SubscriptionBundle bundle2 = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
invoiceChecker.checkChargedThroughDate(baseEntitlement2.getId(), new LocalDate(2012, 5, 1), callContext);
- final DefaultEntitlement baseEntitlement3 = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey3", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement3 = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey3", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
final SubscriptionBundle bundle3 = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
invoiceChecker.checkChargedThroughDate(baseEntitlement2.getId(), new LocalDate(2012, 5, 1), callContext);
// Cancel bundle 2 one day after (2012-05-02)
clock.addDays(1);
- cancelEntitlementAndCheckForCompletion(baseEntitlement2, clock.getUTCNow(), NextEvent.BLOCK, NextEvent.CANCEL);
+ cancelEntitlementAndCheckForCompletion(baseEntitlement2, NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.NULL_INVOICE);
final SubscriptionBase cancelledBaseSubscription2 = ((DefaultEntitlement) entitlementApi.getEntitlementForId(baseEntitlement2.getId(), callContext)).getSubscriptionBase();
assertTrue(cancelledBaseSubscription2.getState() == EntitlementState.CANCELLED);
// DAY 30 have to get out of trial before first payment (2012-05-31)
- addDaysAndCheckForCompletion(29, NextEvent.PHASE, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+ addDaysAndCheckForCompletion(29, NextEvent.PHASE, NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
invoiceChecker.checkInvoice(account.getId(),
4,
callContext,
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithTags.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithTags.java
index 3385436..797eac4 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithTags.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithTags.java
@@ -69,9 +69,11 @@ public class TestOverdueWithTags extends TestOverdueBase {
public void testOverdueStateWith_WRITTEN_OFF() throws Exception {
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+ setupAccount();
+
// Set next invoice to fail and create subscription
paymentPlugin.makeAllInvoicesFailWithError(true);
- final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -80,7 +82,7 @@ public class TestOverdueWithTags extends TestOverdueBase {
// DAY 30 have to get out of trial before first payment
addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
- final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2);
final Invoice nonNullInvoice = invoices.get(1);
@@ -106,9 +108,10 @@ public class TestOverdueWithTags extends TestOverdueBase {
@Test(groups = "slow")
public void testNonOverdueAccountWith_OVERDUE_ENFORCEMENT_OFF() throws Exception {
-
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+ setupAccount();
+
// Set the OVERDUE_ENFORCEMENT_OFF tag (we set the clear state, hence the blocking event)
busHandler.pushExpectedEvents(NextEvent.TAG, NextEvent.BLOCK);
tagUserApi.addTag(account.getId(), ObjectType.ACCOUNT, ControlTagType.OVERDUE_ENFORCEMENT_OFF.getId(), callContext);
@@ -116,7 +119,7 @@ public class TestOverdueWithTags extends TestOverdueBase {
// Set next invoice to fail and create subscription
paymentPlugin.makeAllInvoicesFailWithError(true);
- final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -143,12 +146,13 @@ public class TestOverdueWithTags extends TestOverdueBase {
@Test(groups = "slow")
public void testOverdueAccountWithOverdueEnforcementOffTag() throws Exception {
-
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+ setupAccount();
+
// Set next invoice to fail and create subscription
paymentPlugin.makeAllInvoicesFailWithError(true);
- final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestBundleTransfer.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestBundleTransfer.java
index 7915e6e..648d522 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestBundleTransfer.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestBundleTransfer.java
@@ -55,25 +55,22 @@ public class TestBundleTransfer extends TestIntegrationBase {
@Test(groups = "slow")
public void testBundleTransferWithBPAnnualOnly() throws Exception {
-
- final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(3));
-
// Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
-
final DateTime initialDate = new DateTime(2012, 4, 1, 0, 15, 42, 0, testTimeZone);
-
clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+ final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(3));
+
final String productName = "Shotgun";
final BillingPeriod term = BillingPeriod.ANNUAL;
final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+ assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.ANNUAL);
// Move out of trials for interesting invoices adjustments
@@ -88,41 +85,37 @@ public class TestBundleTransfer extends TestIntegrationBase {
transferApi.transferBundle(account.getId(), newAccount.getId(), "externalKey", clock.getUTCNow(), false, false, callContext);
assertListenerStatus();
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(newAccount.getId(), callContext);
+ final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(newAccount.getId(), false, callContext);
assertEquals(invoices.size(), 1);
final List<InvoiceItem> invoiceItems = invoices.get(0).getInvoiceItems();
assertEquals(invoiceItems.size(), 1);
- InvoiceItem theItem = invoiceItems.get(0);
+ final InvoiceItem theItem = invoiceItems.get(0);
assertTrue(theItem.getStartDate().compareTo(new LocalDate(2012, 5, 11)) == 0);
assertTrue(theItem.getEndDate().compareTo(new LocalDate(2013, 5, 11)) == 0);
assertTrue(theItem.getAmount().compareTo(new BigDecimal("2399.9500")) == 0);
checkNoMoreInvoiceToGenerate(account);
-
}
@Test(groups = "slow")
public void testBundleTransferWithBPMonthlyOnly() throws Exception {
-
- final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
-
// Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
-
final DateTime initialDate = new DateTime(2012, 4, 1, 0, 15, 42, 0, testTimeZone);
-
clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+ final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
+
final String productName = "Shotgun";
final BillingPeriod term = BillingPeriod.MONTHLY;
final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
assertListenerStatus();
- assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+ assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.MONTHLY);
@@ -145,7 +138,7 @@ public class TestBundleTransfer extends TestIntegrationBase {
// Day of the transfer
assertEquals(newBCD, (Integer) 3);
- final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(newAccount.getId(), callContext);
+ final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(newAccount.getId(), false, callContext);
assertEquals(invoices.size(), 1);
final List<InvoiceItem> invoiceItems = invoices.get(0).getInvoiceItems();
@@ -161,26 +154,24 @@ public class TestBundleTransfer extends TestIntegrationBase {
@Test(groups = "slow")
public void testBundleTransferWithBPMonthlyOnlyWIthCancellationImm() throws Exception {
-
- final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(9));
-
// Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
-
final DateTime initialDate = new DateTime(2012, 4, 1, 0, 15, 42, 0, testTimeZone);
-
clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+ final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(9));
+
final String productName = "Shotgun";
final BillingPeriod term = BillingPeriod.MONTHLY;
final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
final PlanPhaseSpecifier bpPlanPhaseSpecifier = new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null);
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
assertListenerStatus();
- assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+ assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.MONTHLY);
@@ -196,7 +187,7 @@ public class TestBundleTransfer extends TestIntegrationBase {
transferApi.transferBundle(account.getId(), newAccount.getId(), "externalKey", clock.getUTCNow(), false, true, callContext);
assertListenerStatus();
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 3);
// CHECK OLD AND NEW ACCOUNTS ITEMS
@@ -211,7 +202,7 @@ public class TestBundleTransfer extends TestIntegrationBase {
invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, toBeChecked);
// CHECK NEW ACCOUNT ITEMS
- invoices = invoiceUserApi.getInvoicesByAccount(newAccount.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(newAccount.getId(), false, callContext);
assertEquals(invoices.size(), 1);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -238,18 +229,18 @@ public class TestBundleTransfer extends TestIntegrationBase {
// Create the base plan
final String bundleExternalKey = UUID.randomUUID().toString();
final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), bundleExternalKey, bpProductName, ProductCategory.BASE, term,
- NextEvent.CREATE, NextEvent.INVOICE);
+ NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
subscriptionChecker.checkSubscriptionCreated(bpEntitlement.getId(), internalCallContext);
final Invoice firstInvoice = invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
// Create the add-on
final DefaultEntitlement aoEntitlement = addAOEntitlementAndCheckForCompletion(bpEntitlement.getBundleId(), aoProductName, ProductCategory.ADD_ON, term,
- NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
final Invoice secondInvoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("399.95")));
paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 4, 1), new BigDecimal("399.95"), TransactionStatus.SUCCESS, secondInvoice.getId(), Currency.USD));
// Move past the phase for simplicity
- busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(30);
assertListenerStatus();
final Invoice thirdInvoice = invoiceChecker.checkInvoice(account.getId(), 3, callContext,
@@ -270,7 +261,7 @@ public class TestBundleTransfer extends TestIntegrationBase {
final DateTime now = clock.getUTCNow();
final LocalDate transferDay = now.toLocalDate();
- busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.BLOCK, NextEvent.TRANSFER, NextEvent.TRANSFER, NextEvent.INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.BLOCK, NextEvent.TRANSFER, NextEvent.TRANSFER, NextEvent.BLOCK, NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
final UUID newBundleId = entitlementApi.transferEntitlements(account.getId(), newAccount.getId(), bundleExternalKey, transferDay, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
@@ -313,6 +304,5 @@ public class TestBundleTransfer extends TestIntegrationBase {
}
checkNoMoreInvoiceToGenerate(account);
-
}
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java
index 3a8a532..e312a33 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java
@@ -50,7 +50,6 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
@Test(groups = "slow")
public void testRetirePlan() throws Exception {
-
// Catalog v1 starts in 2011-01-01
// Catalog v2 starts in 2015-12-01
final LocalDate today = new LocalDate(2015, 11, 5);
@@ -66,10 +65,10 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
final DefaultEntitlement bpEntitlement =
createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName,
- ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+ assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.MONTHLY);
@@ -81,11 +80,10 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
// Catalog v2 should start now.
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- final LocalDate effectiveDate = new LocalDate(clock.getUTCNow());
try {
- entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
+ entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
fail(); // force to fail is there is not an exception
- } catch (EntitlementApiException e) {
+ } catch (final EntitlementApiException e) {
assertTrue(e.getLocalizedMessage().startsWith("Could not find a plan matching: (product: 'Pistol', billing period: 'MONTHLY'"));
}
@@ -94,17 +92,15 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
clock.addMonths(1);
assertListenerStatus();
- final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 3);
- for (Invoice invoice : invoices) {
+ for (final Invoice invoice : invoices) {
assertEquals(invoice.getInvoiceItems().get(0).getPlanName(), "pistol-monthly");
}
-
}
@Test(groups = "slow")
public void testRetireProduct() throws Exception {
-
// Catalog v1 starts in 2011-01-01
// Catalog v3 starts in 2016-01-01
final LocalDate today = new LocalDate(2015, 11, 5);
@@ -120,10 +116,10 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
final DefaultEntitlement bpEntitlement =
createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName,
- ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+ assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.MONTHLY);
@@ -140,11 +136,10 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
// Catalog v3 should start now.
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- final LocalDate effectiveDate = new LocalDate(clock.getUTCNow());
try {
- entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
+ entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
fail(); // force to fail is there is not an exception
- } catch (EntitlementApiException e) {
+ } catch (final EntitlementApiException e) {
assertTrue(e.getLocalizedMessage().startsWith("Could not find any product named 'Pistol'"));
}
@@ -153,17 +148,15 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
clock.addMonths(1);
assertListenerStatus();
- final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 4);
- for (Invoice invoice : invoices) {
+ for (final Invoice invoice : invoices) {
assertEquals(invoice.getInvoiceItems().get(0).getPlanName(), "pistol-monthly");
}
-
}
@Test(groups = "slow")
public void testRetirePriceList() throws Exception {
-
// Catalog v1 starts in 2011-01-01
// Catalog v2 starts in 2015-12-01
// Catalog v3 starts in 2016-01-01
@@ -178,14 +171,13 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
final String productName = "Pistol";
final BillingPeriod term = BillingPeriod.MONTHLY;
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, "SpecialDiscount", null);
- LocalDate effectiveDate = new LocalDate(clock.getUTCNow());
- busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.INVOICE);
- final Entitlement bpEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey", null, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
+ busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+ final Entitlement bpEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey", null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
assertNotNull(bpEntitlement);
- assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+ assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
// Move out a month.
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
@@ -199,11 +191,10 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
// PriceList "SpecialDiscount" at this point.
- effectiveDate = new LocalDate(clock.getUTCNow());
try {
- entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
+ entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
fail(); // force to fail is there is not an exception
- } catch (EntitlementApiException e) {
+ } catch (final EntitlementApiException e) {
assertTrue(e.getLocalizedMessage().startsWith("Could not find any product named 'Pistol'"));
}
@@ -213,11 +204,11 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
assertListenerStatus();
// Move out a month.
- busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addMonths(1);
assertListenerStatus();
- final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 5);
assertTrue(invoices.get(0).getInvoiceItems().get(0).getPhaseName().equals("discount-pistol-monthly-trial"));
@@ -225,7 +216,5 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
assertTrue(invoices.get(2).getInvoiceItems().get(0).getPhaseName().equals("discount-pistol-monthly-discount"));
assertTrue(invoices.get(3).getInvoiceItems().get(0).getPhaseName().equals("discount-pistol-monthly-discount"));
assertTrue(invoices.get(4).getInvoiceItems().get(0).getPhaseName().equals("discount-pistol-monthly-evergreen"));
-
}
-
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCustomFieldApi.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCustomFieldApi.java
index ed6564c..c6976cd 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCustomFieldApi.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCustomFieldApi.java
@@ -83,10 +83,10 @@ public class TestCustomFieldApi extends TestIntegrationBase {
final BillingPeriod term = BillingPeriod.ANNUAL;
final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
Assert.assertEquals(invoices.size(), 1);
final Invoice invoice = invoices.get(0);
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 b1ff283..acee264 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
@@ -24,15 +24,14 @@ 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.ErrorCode;
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.beatrix.util.PaymentChecker.ExpectedPaymentCheck;
-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.PriceListSet;
@@ -47,7 +46,6 @@ 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;
@@ -65,28 +63,27 @@ public class TestIntegration extends TestIntegrationBase {
@Test(groups = "slow")
public void testCancelBPWithAOTheSameDay() throws Exception {
+ // We take april as it has 30 days (easier to play with BCD)
+ // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+ clock.setDay(new LocalDate(2012, 4, 1));
final AccountData accountData = getAccountData(1);
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
accountChecker.checkAccount(account.getId(), accountData, callContext);
- // We take april as it has 30 days (easier to play with BCD)
- // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
- clock.setDay(new LocalDate(2012, 4, 1));
-
final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
TestDryRunArguments dryRun = new TestDryRunArguments(DryRunType.SUBSCRIPTION_ACTION, "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, null, null,
- SubscriptionEventType.START_BILLING, null, null, clock.getUTCNow(), null);
+ SubscriptionEventType.START_BILLING, null, null, null, null);
Invoice dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 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);
+ final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, 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);
@@ -96,14 +93,14 @@ public class TestIntegration extends TestIntegrationBase {
// ADD ADD_ON ON THE SAME DAY
//
dryRun = new TestDryRunArguments(DryRunType.SUBSCRIPTION_ACTION, "Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, null, null,
- SubscriptionEventType.START_BILLING, null, bpSubscription.getBundleId(), clock.getUTCNow(), null);
+ SubscriptionEventType.START_BILLING, null, bpSubscription.getBundleId(), null, null);
dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("399.95")));
invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
- addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
- Invoice invoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext, expectedInvoices);
+ final Invoice invoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext, expectedInvoices);
paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 4, 1), new BigDecimal("399.95"), TransactionStatus.SUCCESS, invoice.getId(), Currency.USD));
expectedInvoices.clear();
@@ -112,7 +109,7 @@ public class TestIntegration extends TestIntegrationBase {
// There is no invoice created as we only adjust the previous invoice.
//
dryRun = new TestDryRunArguments(DryRunType.SUBSCRIPTION_ACTION, null, null, null, null, null, SubscriptionEventType.STOP_BILLING, bpSubscription.getId(),
- bpSubscription.getBundleId(), clock.getUTCNow(), null);
+ bpSubscription.getBundleId(), null, null);
dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-399.95")));
// The second invoice should be adjusted for the AO (we paid for the full period) and since we paid we should also see a CBA
@@ -121,35 +118,32 @@ public class TestIntegration extends TestIntegrationBase {
invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
- cancelEntitlementAndCheckForCompletion(bpSubscription, clock.getUTCNow(), NextEvent.BLOCK, NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.CANCEL, NextEvent.INVOICE);
+ cancelEntitlementAndCheckForCompletion(bpSubscription, NextEvent.BLOCK, NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.CANCEL, NextEvent.INVOICE);
invoiceChecker.checkInvoice(account.getId(), 3,
callContext,
expectedInvoices);
checkNoMoreInvoiceToGenerate(account);
-
}
@Test(groups = "slow")
public void testBasePlanCompleteWithBillingDayInPast() throws Exception {
-
final int billingDay = 31;
final DateTime initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
+ // set clock to the initial start date
+ clock.setTime(initialCreationDate);
- log.info("Beginning test with BCD of " + billingDay);
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
- // set clock to the initial start date
- clock.setTime(initialCreationDate);
int invoiceItemCount = 1;
final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
DefaultSubscriptionBase subscription = subscriptionDataFromSubscription(baseEntitlement.getSubscriptionBase());
invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
// No end date for the trial item (fixed price of zero), and CTD should be today (i.e. when the trial started)
@@ -159,7 +153,7 @@ public class TestIntegration extends TestIntegrationBase {
// CHANGE PLAN IMMEDIATELY AND EXPECT BOTH EVENTS: NextEvent.CHANGE NextEvent.INVOICE
//
TestDryRunArguments dryRun = new TestDryRunArguments(DryRunType.SUBSCRIPTION_ACTION, "Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, null, null, SubscriptionEventType.CHANGE,
- subscription.getId(), subscription.getBundleId(), clock.getUTCNow(), null);
+ subscription.getId(), subscription.getBundleId(), null, null);
Invoice dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
expectedInvoices.add(new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
@@ -213,7 +207,7 @@ public class TestIntegration extends TestIntegrationBase {
invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
- addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, expectedInvoices);
invoiceChecker.checkChargedThroughDate(subscription.getId(), secondRecurringPistolDate, callContext);
expectedInvoices.clear();
@@ -236,34 +230,30 @@ public class TestIntegration extends TestIntegrationBase {
//
// FINALLY CANCEL SUBSCRIPTION EOT
//
- baseEntitlement = cancelEntitlementAndCheckForCompletion(baseEntitlement, clock.getUTCNow(), NextEvent.BLOCK);
+ baseEntitlement = cancelEntitlementAndCheckForCompletion(baseEntitlement, NextEvent.BLOCK);
// MOVE AFTER CANCEL DATE AND EXPECT EVENT : NextEvent.CANCEL
- addDaysAndCheckForCompletion(31, NextEvent.CANCEL);
+ addDaysAndCheckForCompletion(31, NextEvent.CANCEL, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 7, 31), callContext);
checkNoMoreInvoiceToGenerate(account);
-
- log.info("TEST PASSED !");
}
@Test(groups = "slow")
public void testBasePlanCompleteWithBillingDayAlignedWithTrial() throws Exception {
-
final int billingDay = 2;
final DateTime initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
+ // set clock to the initial start date
+ clock.setTime(initialCreationDate);
- log.info("Beginning test with BCD of " + billingDay);
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
- // set clock to the initial start date
- clock.setTime(initialCreationDate);
int invoiceItemCount = 1;
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
DefaultSubscriptionBase subscription = subscriptionDataFromSubscription(baseEntitlement.getSubscriptionBase());
invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
// No end date for the trial item (fixed price of zero), and CTD should be today (i.e. when the trial started)
@@ -292,17 +282,15 @@ public class TestIntegration extends TestIntegrationBase {
//
- TestDryRunArguments dryRun = new TestDryRunArguments(DryRunType.SUBSCRIPTION_ACTION, "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, null, null, SubscriptionEventType.CHANGE,
- subscription.getId(), subscription.getBundleId(), null, null);
+ final TestDryRunArguments dryRun = new TestDryRunArguments(DryRunType.SUBSCRIPTION_ACTION, "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, null, null, SubscriptionEventType.CHANGE,
+ subscription.getId(), subscription.getBundleId(), null, null);
try {
invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
Assert.fail("Call should return no invoices");
- } catch (InvoiceApiException e) {
- System.out.print("foo");
-
+ } catch (final InvoiceApiException e) {
+ assertEquals(e.getCode(), ErrorCode.INVOICE_NOTHING_TO_DO.getCode());
}
-
baseEntitlement = changeEntitlementAndCheckForCompletion(baseEntitlement, "Pistol", BillingPeriod.MONTHLY, null);
subscription = subscriptionDataFromSubscription(baseEntitlement.getSubscriptionBase());
@@ -311,7 +299,7 @@ public class TestIntegration extends TestIntegrationBase {
//
final LocalDate firstRecurringPistolDate = subscription.getChargedThroughDate().toLocalDate();
final LocalDate secondRecurringPistolDate = firstRecurringPistolDate.plusMonths(1);
- addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 2), new LocalDate(2012, 5, 2), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
invoiceChecker.checkChargedThroughDate(subscription.getId(), secondRecurringPistolDate, callContext);
@@ -333,33 +321,30 @@ public class TestIntegration extends TestIntegrationBase {
//
// FINALLY CANCEL SUBSCRIPTION EOT
//
- cancelEntitlementAndCheckForCompletion(baseEntitlement, clock.getUTCNow(), NextEvent.BLOCK);
+ cancelEntitlementAndCheckForCompletion(baseEntitlement, NextEvent.BLOCK);
- // MOVE AFTER CANCEL DATE AND EXPECT EVENT : NextEvent.CANCEL
- addDaysAndCheckForCompletion(31, NextEvent.CANCEL);
+ // MOVE AFTER CANCEL DATE
+ addDaysAndCheckForCompletion(31, NextEvent.CANCEL, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 8, 2), callContext);
checkNoMoreInvoiceToGenerate(account);
-
}
@Test(groups = "slow")
public void testBasePlanCompleteWithBillingDayInFuture() throws Exception {
-
final int billingDay = 3;
final DateTime initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
+ // set clock to the initial start date
+ clock.setTime(initialCreationDate);
- log.info("Beginning test with BCD of " + billingDay);
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
- // set clock to the initial start date
- clock.setTime(initialCreationDate);
int invoiceItemCount = 1;
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
DefaultSubscriptionBase subscription = subscriptionDataFromSubscription(baseEntitlement.getSubscriptionBase());
invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -401,7 +386,7 @@ public class TestIntegration extends TestIntegrationBase {
//
final LocalDate firstRecurringPistolDate = subscription.getChargedThroughDate().toLocalDate();
final LocalDate secondRecurringPistolDate = firstRecurringPistolDate.plusMonths(1);
- addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 3), new LocalDate(2012, 5, 3), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
invoiceChecker.checkChargedThroughDate(subscription.getId(), secondRecurringPistolDate, callContext);
@@ -423,16 +408,13 @@ public class TestIntegration extends TestIntegrationBase {
//
// FINALLY CANCEL SUBSCRIPTION EOT
//
- baseEntitlement = cancelEntitlementAndCheckForCompletion(baseEntitlement, clock.getUTCNow(), NextEvent.BLOCK);
+ baseEntitlement = cancelEntitlementAndCheckForCompletion(baseEntitlement, NextEvent.BLOCK);
// MOVE AFTER CANCEL DATE AND EXPECT EVENT : NextEvent.CANCEL
- addDaysAndCheckForCompletion(31, NextEvent.CANCEL);
+ addDaysAndCheckForCompletion(31, NextEvent.CANCEL, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 8, 3), callContext);
checkNoMoreInvoiceToGenerate(account);
-
- log.info("TEST PASSED !");
-
}
@Test(groups = {"stress"}, enabled = false)
@@ -490,50 +472,42 @@ public class TestIntegration extends TestIntegrationBase {
final BillingPeriod term = BillingPeriod.MONTHLY;
final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
- final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
// MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
final Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
- log.info("Moving clock from" + clock.getUTCNow() + " to " + clock.getUTCNow().plusDays(3));
clock.addDays(3);
final DefaultEntitlement aoEntitlement1 = addAOEntitlementAndCheckForCompletion(baseEntitlement.getBundleId(), "Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY,
- NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
final DefaultEntitlement aoEntitlement2 = addAOEntitlementAndCheckForCompletion(baseEntitlement.getBundleId(), "Laser-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY,
- NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
// MOVE CLOCK A LITTLE BIT MORE -- EITHER STAY IN TRIAL OR GET OUT
- busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
- log.info("Moving clock from" + clock.getUTCNow() + " to " + clock.getUTCNow().plusDays(28));
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(28);// 26 / 5
assertListenerStatus();
- busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
- log.info("Moving clock from" + clock.getUTCNow() + " to " + clock.getUTCNow().plusDays(3));
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(3);// 29 / 5
assertListenerStatus();
- log.info("Moving clock from" + clock.getUTCNow() + " to " + clock.getUTCNow().plusDays(10));
clock.addDays(10);// 8 / 6
assertListenerStatus();
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
- log.info("Moving clock from" + clock.getUTCNow() + " to " + clock.getUTCNow().plusDays(18));
clock.addDays(18);// 26 / 6
assertListenerStatus();
- log.info("Moving clock from" + clock.getUTCNow() + " to " + clock.getUTCNow().plusDays(3));
clock.addDays(3);
assertListenerStatus();
checkNoMoreInvoiceToGenerate(account);
-
}
@Test(groups = "slow")
public void testCreateMultipleBPWithSameExternalKey() throws Exception {
-
final DateTime initialDate = new DateTime(2012, 4, 25, 0, 13, 42, 0, testTimeZone);
clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
@@ -543,15 +517,15 @@ public class TestIntegration extends TestIntegrationBase {
final String productName = "Shotgun";
final BillingPeriod term = BillingPeriod.MONTHLY;
- final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
final SubscriptionBundle initialBundle = subscriptionApi.getActiveSubscriptionBundleForExternalKey("bundleKey", callContext);
- busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.CANCEL);
+ busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.NULL_INVOICE);
baseEntitlement.cancelEntitlementWithPolicy(EntitlementActionPolicy.IMMEDIATE, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
final String newProductName = "Pistol";
- final DefaultEntitlement newBaseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", newProductName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement newBaseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", newProductName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
final SubscriptionBundle newBundle = subscriptionApi.getActiveSubscriptionBundleForExternalKey("bundleKey", callContext);
@@ -565,36 +539,33 @@ public class TestIntegration extends TestIntegrationBase {
assertEquals(newBaseEntitlement.getState(), EntitlementState.ACTIVE);
checkNoMoreInvoiceToGenerate(account);
-
}
@Test(groups = "slow")
public void testWithPauseResume() throws Exception {
final DateTime initialDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
final int billingDay = 2;
+ // set clock to the initial start date
+ clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
- log.info("Beginning test with BCD of " + billingDay);
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
final UUID accountId = account.getId();
assertNotNull(account);
- // set clock to the initial start date
- clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
-
- String productName = "Shotgun";
- BillingPeriod term = BillingPeriod.MONTHLY;
- String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+ final String productName = "Shotgun";
+ final BillingPeriod term = BillingPeriod.MONTHLY;
+ final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(baseEntitlement);
//
// VERIFY CTD HAS BEEN SET
//
- DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) baseEntitlement.getSubscriptionBase();
+ final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) baseEntitlement.getSubscriptionBase();
final DateTime startDate = subscription.getCurrentPhaseStart();
final BigDecimal rate = subscription.getCurrentPhase().getFixed().getPrice().getPrice(Currency.USD);
verifyTestResult(accountId, subscription.getId(), startDate, null, rate, clock.getUTCNow(), 1);
@@ -610,8 +581,7 @@ public class TestIntegration extends TestIntegrationBase {
// PAUSE THE ENTITLEMENT
DefaultEntitlement entitlement = (DefaultEntitlement) entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext);
- busHandler.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK);
- busHandler.pushExpectedEvent(NextEvent.INVOICE);
+ busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.INVOICE);
entitlementApi.pause(entitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
@@ -628,8 +598,7 @@ public class TestIntegration extends TestIntegrationBase {
// MOVE CLOCK FORWARD ADN CHECK THERE IS NO NEW INVOICE
clock.addDeltaFromReality(AT_LEAST_ONE_MONTH_MS);
- busHandler.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK);
- busHandler.pushExpectedEvent(NextEvent.INVOICE);
+ busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.INVOICE);
entitlementApi.resume(entitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
@@ -638,14 +607,10 @@ public class TestIntegration extends TestIntegrationBase {
new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 5), new LocalDate(2012, 4, 5), InvoiceItemType.CBA_ADJ, new BigDecimal("-224.96")));
checkNoMoreInvoiceToGenerate(account);
-
}
@Test(groups = "slow")
public void testForMultipleRecurringPhases() throws Exception {
-
- log.info("Starting testForMultipleRecurringPhases");
-
final DateTime initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
clock.setDeltaFromReality(initialCreationDate.getMillis() - clock.getUTCNow().getMillis());
@@ -655,16 +620,16 @@ public class TestIntegration extends TestIntegrationBase {
final String productName = "Blowdart";
final String planSetName = "DEFAULT";
- final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId, callContext);
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId, false, callContext);
assertNotNull(invoices);
assertTrue(invoices.size() == 1);
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDeltaFromReality(AT_LEAST_ONE_MONTH_MS);
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(accountId, callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(accountId, false, callContext);
assertNotNull(invoices);
assertEquals(invoices.size(), 2);
@@ -675,11 +640,11 @@ public class TestIntegration extends TestIntegrationBase {
assertListenerStatus();
}
- busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDeltaFromReality(AT_LEAST_ONE_MONTH_MS);
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(accountId, callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(accountId, false, callContext);
assertNotNull(invoices);
assertEquals(invoices.size(), 8);
@@ -690,7 +655,7 @@ public class TestIntegration extends TestIntegrationBase {
assertListenerStatus();
}
- invoices = invoiceUserApi.getInvoicesByAccount(accountId, callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(accountId, false, callContext);
assertNotNull(invoices);
assertEquals(invoices.size(), 14);
@@ -698,4 +663,73 @@ public class TestIntegration extends TestIntegrationBase {
checkNoMoreInvoiceToGenerate(account);
}
+
+ @Test(groups = "slow")
+ public void testFixedTermPlan() throws Exception {
+ // We take april as it has 30 days (easier to play with BCD)
+ // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+ clock.setDay(new LocalDate(2015, 4, 1));
+
+ final AccountData accountData = getAccountData(1);
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+
+ // First invoice
+ final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+
+ // Second invoice -> first recurring for Refurbish-Maintenance
+ addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Refurbish-Maintenance", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+ final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+
+ expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("599.95")));
+ expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 4, 1), new LocalDate(2015, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("199.95")));
+ invoiceChecker.checkInvoice(account.getId(), 2, callContext, expectedInvoices);
+ expectedInvoices.clear();
+
+
+ expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 5, 1), new LocalDate(2015, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("199.95")));
+ expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 5, 1), new LocalDate(2015, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+
+ // 2015-5-1
+ // Third invoice -> second recurring for Refurbish-Maintenance
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ clock.addDays(30); // Also = 1 month because or initial date 2015, 4, 1
+ assertListenerStatus();
+
+ invoiceChecker.checkInvoice(account.getId(), 3, callContext, expectedInvoices);
+ expectedInvoices.clear();
+
+
+ // Next 10 invoices -> third to twelve **and last** recurring for Refurbish-Maintenance
+ LocalDate startDate = new LocalDate(2015, 6, 1);
+ for (int i = 0; i < 10; i++) {
+
+ final LocalDate endDate = startDate.plusMonths(1);
+ expectedInvoices.add(new ExpectedInvoiceItemCheck(startDate, endDate, InvoiceItemType.RECURRING, new BigDecimal("199.95")));
+ expectedInvoices.add(new ExpectedInvoiceItemCheck(startDate, endDate, InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+
+ busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ clock.addMonths(1);
+ assertListenerStatus();
+
+ invoiceChecker.checkInvoice(account.getId(), 4 + i, callContext, expectedInvoices);
+ expectedInvoices.clear();
+
+ startDate = endDate;
+ }
+
+ // We check there is no more recurring for Refurbish-Maintenance
+ expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 4, 1), new LocalDate(2016, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+
+ busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ clock.addMonths(1);
+ assertListenerStatus();
+
+
+ invoiceChecker.checkInvoice(account.getId(), 14, callContext, expectedInvoices);
+ expectedInvoices.clear();
+ }
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
index e8a325e..d2e8b3b 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -20,6 +20,7 @@ package org.killbill.billing.beatrix.integration;
import java.math.BigDecimal;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -99,6 +100,8 @@ import org.killbill.billing.util.api.RecordIdApi;
import org.killbill.billing.util.api.TagApiException;
import org.killbill.billing.util.api.TagDefinitionApiException;
import org.killbill.billing.util.api.TagUserApi;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.nodes.KillbillNodesApi;
import org.killbill.billing.util.tag.ControlTagType;
import org.killbill.billing.util.tag.Tag;
@@ -122,6 +125,7 @@ import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
+
public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
protected static final DateTimeZone testTimeZone = DateTimeZone.UTC;
@@ -245,6 +249,8 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
@Inject
protected IDBI idbi;
+ @Inject
+ protected NonEntityDao nonEntityDao;
@Inject
protected TestApiListener busHandler;
@@ -258,6 +264,9 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
@Inject
protected KillbillNodesApi nodesApi;
+ @Inject
+ protected CacheControllerDispatcher controllerDispatcher;
+
protected void assertListenerStatus() {
busHandler.assertListenerStatus();
}
@@ -276,6 +285,9 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
//Thread.currentThread().setContextClassLoader(null);
log.debug("RESET TEST FRAMEWORK");
+
+ controllerDispatcher.clearAll();
+
overdueConfigCache.loadDefaultOverdueConfig((OverdueConfig) null);
clock.resetDeltaFromReality();
@@ -307,10 +319,12 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
}
protected void checkNoMoreInvoiceToGenerate(final Account account) {
+ busHandler.pushExpectedEvent(NextEvent.NULL_INVOICE);
try {
invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), null, callContext);
fail("Should not have generated an extra invoice");
} catch (final InvoiceApiException e) {
+ assertListenerStatus();
assertEquals(e.getCode(), ErrorCode.INVOICE_NOTHING_TO_DO.getCode());
}
}
@@ -347,6 +361,8 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
final Account account = accountUserApi.createAccount(accountData, callContext);
assertNotNull(account);
+ refreshCallContext(account.getId());
+
final PaymentMethodPlugin info = createPaymentMethodPlugin();
paymentApi.addPaymentMethod(account, UUID.randomUUID().toString(), paymentPluginName, true, info, PLUGIN_PROPERTIES, callContext);
@@ -430,7 +446,6 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
@Override
public Payment apply(@Nullable final Void input) {
try {
-
final List<PluginProperty> properties = new ArrayList<PluginProperty>();
final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, invoice.getId().toString(), false);
properties.add(prop1);
@@ -484,11 +499,15 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
}
protected Payment refundPaymentAndCheckForCompletion(final Account account, final Payment payment, final NextEvent... events) {
+ return refundPaymentAndCheckForCompletion(account, payment, payment.getPurchasedAmount(), payment.getCurrency(), events);
+ }
+
+ protected Payment refundPaymentAndCheckForCompletion(final Account account, final Payment payment, final BigDecimal amount, final Currency currency, final NextEvent... events) {
return doCallAndCheckForCompletion(new Function<Void, Payment>() {
@Override
public Payment apply(@Nullable final Void input) {
try {
- return paymentApi.createRefundWithPaymentControl(account, payment.getId(), payment.getPurchasedAmount(), payment.getCurrency(), UUID.randomUUID().toString(),
+ return paymentApi.createRefundWithPaymentControl(account, payment.getId(), amount, currency, UUID.randomUUID().toString(),
PLUGIN_PROPERTIES, PAYMENT_OPTIONS, callContext);
} catch (final PaymentApiException e) {
fail(e.toString());
@@ -518,18 +537,21 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
}
protected Payment refundPaymentWithInvoiceItemAdjAndCheckForCompletion(final Account account, final Payment payment, final Map<UUID, BigDecimal> iias, final NextEvent... events) {
+ return refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment, payment.getPurchasedAmount(), payment.getCurrency(), iias, events);
+ }
+
+ protected Payment refundPaymentWithInvoiceItemAdjAndCheckForCompletion(final Account account, final Payment payment, final BigDecimal amount, final Currency currency, final Map<UUID, BigDecimal> iias, final NextEvent... events) {
return doCallAndCheckForCompletion(new Function<Void, Payment>() {
@Override
public Payment apply(@Nullable final Void input) {
-
- final List<PluginProperty> properties = new ArrayList<PluginProperty>();
+ final Collection<PluginProperty> properties = new ArrayList<PluginProperty>();
final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_REFUND_WITH_ADJUSTMENTS, "true", false);
properties.add(prop1);
final PluginProperty prop2 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_REFUND_IDS_WITH_AMOUNT_KEY, iias, false);
properties.add(prop2);
try {
- return paymentApi.createRefundWithPaymentControl(account, payment.getId(), payment.getPurchasedAmount(), payment.getCurrency(), UUID.randomUUID().toString(),
+ return paymentApi.createRefundWithPaymentControl(account, payment.getId(), amount, currency, UUID.randomUUID().toString(),
properties, PAYMENT_OPTIONS, callContext);
} catch (final PaymentApiException e) {
fail(e.toString());
@@ -539,18 +561,21 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
}, events);
}
- protected void createChargeBackAndCheckForCompletion(final Account account, final Payment payment, final NextEvent... events) {
- doCallAndCheckForCompletion(new Function<Void, Void>() {
+ protected Payment createChargeBackAndCheckForCompletion(final Account account, final Payment payment, final NextEvent... events) {
+ return createChargeBackAndCheckForCompletion(account, payment, payment.getPurchasedAmount(), payment.getCurrency(), events);
+ }
+
+ protected Payment createChargeBackAndCheckForCompletion(final Account account, final Payment payment, final BigDecimal amount, final Currency currency, final NextEvent... events) {
+ return doCallAndCheckForCompletion(new Function<Void, Payment>() {
@Override
- public Void apply(@Nullable final Void input) {
+ public Payment apply(@Nullable final Void input) {
try {
- paymentApi.createChargebackWithPaymentControl(account, payment.getId(), payment.getPurchasedAmount(), payment.getCurrency(), UUID.randomUUID().toString(),
- PAYMENT_OPTIONS, callContext);
- } catch (PaymentApiException e) {
+ return paymentApi.createChargebackWithPaymentControl(account, payment.getId(), amount, currency, UUID.randomUUID().toString(),
+ PAYMENT_OPTIONS, callContext);
+ } catch (final PaymentApiException e) {
fail(e.toString());
return null;
}
- return null;
}
}, events);
}
@@ -571,12 +596,11 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
public Entitlement apply(@Nullable final Void dontcare) {
try {
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, productCategory, billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- final LocalDate effectiveDate = new LocalDate(clock.getUTCNow());
- final Entitlement entitlement = entitlementApi.createBaseEntitlement(accountId, spec, bundleExternalKey, overrides, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(accountId, spec, bundleExternalKey, overrides, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertNotNull(entitlement);
return entitlement;
} catch (final EntitlementApiException e) {
- fail();
+ fail(e.toString());
return null;
}
}
@@ -606,8 +630,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
public Entitlement apply(@Nullable final Void dontcare) {
try {
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, productCategory, billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- final LocalDate effectiveDate = new LocalDate(clock.getUTCNow());
- final Entitlement entitlement = entitlementApi.addEntitlement(bundleId, spec, null, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
+ final Entitlement entitlement = entitlementApi.addEntitlement(bundleId, spec, null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertNotNull(entitlement);
return entitlement;
} catch (final EntitlementApiException e) {
@@ -633,7 +656,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
if (billingPolicy == null) {
refreshedEntitlement = refreshedEntitlement.changePlan(productName, billingPeriod, priceList, null, ImmutableList.<PluginProperty>of(), callContext);
} else {
- refreshedEntitlement = refreshedEntitlement.changePlanOverrideBillingPolicy(productName, billingPeriod, priceList, null, clock.getUTCNow().toLocalDate(), billingPolicy, ImmutableList.<PluginProperty>of(), callContext);
+ refreshedEntitlement = refreshedEntitlement.changePlanOverrideBillingPolicy(productName, billingPeriod, priceList, null, null, billingPolicy, ImmutableList.<PluginProperty>of(), callContext);
}
return refreshedEntitlement;
} catch (final EntitlementApiException e) {
@@ -653,7 +676,12 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
}
protected DefaultEntitlement cancelEntitlementAndCheckForCompletion(final Entitlement entitlement,
- final DateTime requestedDate,
+ final NextEvent... events) {
+ return cancelEntitlementAndCheckForCompletion(entitlement, null, events);
+ }
+
+ protected DefaultEntitlement cancelEntitlementAndCheckForCompletion(final Entitlement entitlement,
+ final LocalDate requestedDate,
final NextEvent... events) {
return (DefaultEntitlement) doCallAndCheckForCompletion(new Function<Void, Entitlement>() {
@Override
@@ -661,7 +689,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
try {
// Need to fetch again to get latest CTD updated from the system
Entitlement refreshedEntitlement = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
- refreshedEntitlement = refreshedEntitlement.cancelEntitlementWithDate(requestedDate.toLocalDate(), false, ImmutableList.<PluginProperty>of(), callContext);
+ refreshedEntitlement = refreshedEntitlement.cancelEntitlementWithDate(requestedDate, false, ImmutableList.<PluginProperty>of(), callContext);
return refreshedEntitlement;
} catch (final EntitlementApiException e) {
fail(e.getMessage());
@@ -681,8 +709,10 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
invoice.getId(),
invoiceItem.getId(),
invoice.getInvoiceDate(),
+ null,
callContext);
}
+
} catch (final InvoiceApiException e) {
fail(e.toString());
}
@@ -697,7 +727,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
public Void apply(@Nullable final Void input) {
try {
invoiceUserApi.insertInvoiceItemAdjustment(account.getId(), invoice.getId(), invoice.getInvoiceItems().get(itemNb - 1).getId(),
- invoice.getInvoiceDate(), callContext);
+ invoice.getInvoiceDate(), null, callContext);
} catch (final InvoiceApiException e) {
fail(e.toString());
}
@@ -742,7 +772,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
private final SubscriptionEventType action;
private final UUID subscriptionId;
private final UUID bundleId;
- private final DateTime effectiveDate;
+ private final LocalDate effectiveDate;
private final BillingActionPolicy billingPolicy;
public TestDryRunArguments(final DryRunType dryRunType) {
@@ -764,7 +794,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
final SubscriptionEventType action,
final UUID subscriptionId,
final UUID bundleId,
- final DateTime effectiveDate,
+ final LocalDate effectiveDate,
final BillingActionPolicy billingPolicy) {
this.dryRunType = dryRunType;
this.spec = new PlanPhaseSpecifier(productName, category, billingPeriod, priceList, phaseType);
@@ -796,7 +826,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
}
@Override
- public DateTime getEffectiveDate() {
+ public LocalDate getEffectiveDate() {
return effectiveDate;
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoice.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoice.java
index 6c97c11..5c25f3c 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoice.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoice.java
@@ -58,21 +58,20 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
//
@Test(groups = "slow")
public void testDryRunWithNoTargetDate() throws Exception {
-
final int billingDay = 14;
final DateTime initialCreationDate = new DateTime(2015, 5, 15, 0, 0, 0, 0, testTimeZone);
+ // set clock to the initial start date
+ clock.setTime(initialCreationDate);
log.info("Beginning test with BCD of " + billingDay);
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
- // set clock to the initial start date
- clock.setTime(initialCreationDate);
int invoiceItemCount = 1;
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
DefaultSubscriptionBase subscription = subscriptionDataFromSubscription(baseEntitlement.getSubscriptionBase());
invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
// No end date for the trial item (fixed price of zero), and CTD should be today (i.e. when the trial started)
@@ -90,7 +89,7 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(30);
assertListenerStatus();
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, expectedInvoices);
expectedInvoices.clear();
@@ -104,7 +103,7 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addMonths(1);
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, expectedInvoices);
expectedInvoices.clear();
@@ -114,7 +113,6 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
}
-
//
// More sophisticated test with two non aligned subscriptions that verifies the behavior of using invoice dryRun api with no date
// - The first subscription is an annual (SUBSCRIPTION aligned) whose billingDate is the first (we start on Jan 2nd to take into account the 30 days trial)
@@ -124,20 +122,19 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
//
@Test(groups = "slow")
public void testDryRunWithNoTargetDateAndMultipleNonAlignedSubscriptions() throws Exception {
-
- // billing date for the monthly
- final int billingDay = 14;
-
// Set in such a way that annual billing date will be the 1st
final DateTime initialCreationDate = new DateTime(2014, 1, 2, 0, 0, 0, 0, testTimeZone);
clock.setTime(initialCreationDate);
+ // billing date for the monthly
+ final int billingDay = 14;
+
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
int invoiceItemCount = 1;
// Create ANNUAL BP
- DefaultEntitlement baseEntitlementAnnual = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKeyAnnual", "Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+ DefaultEntitlement baseEntitlementAnnual = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKeyAnnual", "Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
DefaultSubscriptionBase subscriptionAnnual = subscriptionDataFromSubscription(baseEntitlementAnnual.getSubscriptionBase());
invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
// No end date for the trial item (fixed price of zero), and CTD should be today (i.e. when the trial started)
@@ -170,7 +167,7 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
clock.setTime(secondSubscriptionCreationDate);
// Create the monthly
- DefaultEntitlement baseEntitlementMonthly = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ DefaultEntitlement baseEntitlementMonthly = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
DefaultSubscriptionBase subscriptionMonthly = subscriptionDataFromSubscription(baseEntitlementMonthly.getSubscriptionBase());
invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(secondSubscriptionCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
// No end date for the trial item (fixed price of zero), and CTD should be today (i.e. when the trial started)
@@ -214,23 +211,20 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
}
-
@Test(groups = "slow")
public void testApplyCreditOnExistingBalance() throws Exception {
-
+ final DateTime initialCreationDate = new DateTime(2015, 5, 15, 0, 0, 0, 0, testTimeZone);
+ // set clock to the initial start date
+ clock.setTime(initialCreationDate);
final int billingDay = 14;
- final DateTime initialCreationDate = new DateTime(2015, 5, 15, 0, 0, 0, 0, testTimeZone);
log.info("Beginning test with BCD of " + billingDay);
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
add_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
- // set clock to the initial start date
- clock.setTime(initialCreationDate);
-
- createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
// Move through time and verify we get the same invoice
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE);
@@ -248,7 +242,7 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
assertTrue(accountBalance1.compareTo(new BigDecimal("249.95")) == 0);
busHandler.pushExpectedEvents(NextEvent.INVOICE);
- invoiceUserApi.insertCredit(account.getId(), new BigDecimal("300"), new LocalDate(clock.getUTCNow(), account.getTimeZone()), account.getCurrency(), true, callContext);
+ invoiceUserApi.insertCredit(account.getId(), new BigDecimal("300"), new LocalDate(clock.getUTCNow(), account.getTimeZone()), account.getCurrency(), true, null, callContext);
assertListenerStatus();
final BigDecimal accountBalance2 = invoiceUserApi.getAccountBalance(account.getId(), callContext);
@@ -279,18 +273,15 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
final int billingDay = 14;
final DateTime initialCreationDate = new DateTime(2015, 5, 15, 0, 0, 0, 0, testTimeZone);
+ // set clock to the initial start date
+ clock.setTime(initialCreationDate);
log.info("Beginning test with BCD of " + billingDay);
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
- // set clock to the initial start date
- clock.setTime(initialCreationDate);
int invoiceItemCount = 1;
- //
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
- //
- DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
DefaultSubscriptionBase subscription = subscriptionDataFromSubscription(baseEntitlement.getSubscriptionBase());
final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
@@ -301,7 +292,7 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
clock.addDays(30);
assertListenerStatus();
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, expectedInvoices);
expectedInvoices.clear();
@@ -323,7 +314,7 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addMonths(1);
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, expectedDraftInvoices);
invoiceChecker.checkInvoice(invoices.get(3).getId(), callContext, expectedInvoices);
@@ -343,24 +334,24 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
final int billingDay = 14;
final DateTime initialCreationDate = new DateTime(2015, 5, 15, 0, 0, 0, 0, testTimeZone);
+ // set clock to the initial start date
+ clock.setTime(initialCreationDate);
log.info("Beginning test with BCD of " + billingDay);
final Account parentAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
final Account child1Account = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));
final Account child2Account = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));
- // set clock to the initial start date
- clock.setTime(initialCreationDate);
//
// CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
//
- DefaultEntitlement baseEntitlementChild1 = createBaseEntitlementAndCheckForCompletion(child1Account.getId(), "bundleKey1", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
- DefaultEntitlement baseEntitlementChild2 = createBaseEntitlementAndCheckForCompletion(child2Account.getId(), "bundleKey2", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ DefaultEntitlement baseEntitlementChild1 = createBaseEntitlementAndCheckForCompletion(child1Account.getId(), "bundleKey1", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+ DefaultEntitlement baseEntitlementChild2 = createBaseEntitlementAndCheckForCompletion(child2Account.getId(), "bundleKey2", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
// First Parent invoice over TRIAL period
- List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), callContext);
+ List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
assertEquals(parentInvoices.size(), 1);
Invoice parentInvoice = parentInvoices.get(0);
@@ -384,7 +375,7 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
assertListenerStatus();
// Second Parent invoice over Recurring period
- parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), callContext);
+ parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
assertEquals(parentInvoices.size(), 2);
parentInvoice = parentInvoices.get(1);
@@ -394,7 +385,7 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
assertEquals(parentInvoice.getBalance().toString(), "279.90");
// Check Child Balance. It should be > 0 here because Parent didn't pay yet.
- List<Invoice> child1Invoices = invoiceUserApi.getInvoicesByAccount(child1Account.getId(), callContext);
+ List<Invoice> child1Invoices = invoiceUserApi.getInvoicesByAccount(child1Account.getId(), false, callContext);
assertEquals(child1Invoices.size(), 2);
assertTrue(child1Invoices.get(1).getBalance().compareTo(BigDecimal.ZERO) > 0);
@@ -407,7 +398,7 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
// Check Child Balance. It should be = 0 because parent invoice had already paid.
- child1Invoices = invoiceUserApi.getInvoicesByAccount(child1Account.getId(), callContext);
+ child1Invoices = invoiceUserApi.getInvoicesByAccount(child1Account.getId(), false, callContext);
assertEquals(child1Invoices.size(), 2);
assertTrue(parentInvoice.getBalance().compareTo(BigDecimal.ZERO) == 0);
assertTrue(child1Invoices.get(1).getBalance().compareTo(BigDecimal.ZERO) == 0);
@@ -420,17 +411,18 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
final int billingDay = 14;
final DateTime initialCreationDate = new DateTime(2015, 5, 15, 0, 0, 0, 0, testTimeZone);
+ // set clock to the initial start date
+ clock.setTime(initialCreationDate);
+
+
log.info("Beginning test with BCD of " + billingDay);
final Account parentAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
final Account childAccount = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));
- // set clock to the initial start date
- clock.setTime(initialCreationDate);
-
//
// CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
//
- DefaultEntitlement baseEntitlementChild = createBaseEntitlementAndCheckForCompletion(childAccount.getId(), "bundleKey1", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ DefaultEntitlement baseEntitlementChild = createBaseEntitlementAndCheckForCompletion(childAccount.getId(), "bundleKey1", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
// Moving a day the NotificationQ calls the commitInvoice. No payment is expected because balance is 0
busHandler.pushExpectedEvents(NextEvent.INVOICE);
@@ -443,7 +435,7 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
assertListenerStatus();
// check parent Invoice with child plan amount
- List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), callContext);
+ List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
assertEquals(parentInvoices.size(), 2);
Invoice parentInvoice = parentInvoices.get(1);
@@ -458,7 +450,7 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
assertListenerStatus();
// check parent invoice. Expected to have the same invoice item but the amount updated
- parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), callContext);
+ parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
assertEquals(parentInvoices.size(), 2);
parentInvoice = parentInvoices.get(1);
@@ -476,5 +468,4 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
}
-
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java
index e90cf77..b82fdd5 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java
@@ -61,23 +61,23 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
public void testSimplePartialRepairWithItemAdjustment() throws Exception {
// We take april as it has 30 days (easier to play with BCD)
final LocalDate today = new LocalDate(2012, 4, 1);
- final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
-
// Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
clock.setDay(today);
+ final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
final String productName = "Shotgun";
final BillingPeriod term = BillingPeriod.MONTHLY;
final String pricelistName = PriceListSet.DEFAULT_PRICELIST_NAME;
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 1);
ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, BigDecimal.ZERO));
@@ -91,7 +91,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
clock.addDays(31);
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -107,10 +107,10 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
//
busHandler.pushExpectedEvents(NextEvent.INVOICE_ADJUSTMENT);
invoiceUserApi.insertInvoiceItemAdjustment(account.getId(), invoices.get(1).getId(), invoices.get(1).getInvoiceItems().get(0).getId(), clock.getUTCToday(),
- BigDecimal.TEN, account.getCurrency(), callContext);
+ BigDecimal.TEN, account.getCurrency(), null, callContext);
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -120,15 +120,14 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 2), new LocalDate(2012, 5, 2), InvoiceItemType.ITEM_ADJ, new BigDecimal("-10")),
- // TODO PIERRE The cba start_date/end_date are created using the callcontext
- new ExpectedInvoiceItemCheck(callContext.getCreatedDate().toLocalDate(), callContext.getCreatedDate().toLocalDate(), InvoiceItemType.CBA_ADJ, new BigDecimal("10")));
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 2), new LocalDate(2012, 5, 2), InvoiceItemType.CBA_ADJ, new BigDecimal("10")));
invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, toBeChecked);
//
// Force a plan change
//
changeEntitlementAndCheckForCompletion(bpEntitlement, "Blowdart", term, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE);
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 3);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -138,8 +137,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 2), new LocalDate(2012, 5, 2), InvoiceItemType.ITEM_ADJ, new BigDecimal("-10")),
- // TODO PIERRE The cba start_date/end_date are created using the callcontext
- new ExpectedInvoiceItemCheck(callContext.getCreatedDate().toLocalDate(), callContext.getCreatedDate().toLocalDate(), InvoiceItemType.CBA_ADJ, new BigDecimal("10")));
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 2), new LocalDate(2012, 5, 2), InvoiceItemType.CBA_ADJ, new BigDecimal("10")));
invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, toBeChecked);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -157,22 +155,22 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
public void testMultiplePartialRepairs() throws Exception {
// We take april as it has 30 days (easier to play with BCD)
final LocalDate today = new LocalDate(2012, 4, 1);
- final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
-
// Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
clock.setDay(today);
+ final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
final String productName = "Shotgun";
final BillingPeriod term = BillingPeriod.MONTHLY;
final String pricelistName = PriceListSet.DEFAULT_PRICELIST_NAME;
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 1);
ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, BigDecimal.ZERO));
@@ -187,7 +185,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
//
changeEntitlementAndCheckForCompletion(bpEntitlement, "Assault-Rifle", term, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE);
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -206,7 +204,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
clock.addDays(28);
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 3);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -228,7 +226,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
clock.addDays(5);
changeEntitlementAndCheckForCompletion(bpEntitlement, "Blowdart", term, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE);
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 4);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -256,7 +254,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
clock.addDays(1);
changeEntitlementAndCheckForCompletion(bpEntitlement, "Pistol", term, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE);
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 5);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -291,7 +289,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
clock.addMonths(1);
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 6);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -331,7 +329,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
clock.addMonths(1);
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 7);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -373,25 +371,24 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
@Test(groups = "slow")
public void testPartialRepairWithCompleteRefund() throws Exception {
-
// We take april as it has 30 days (easier to play with BCD)
final LocalDate today = new LocalDate(2012, 4, 1);
- final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
-
// Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
clock.setDeltaFromReality(today.toDateTimeAtCurrentTime(DateTimeZone.UTC).getMillis() - clock.getUTCNow().getMillis());
+ final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
final String productName = "Shotgun";
final BillingPeriod term = BillingPeriod.ANNUAL;
final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+ assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.ANNUAL);
@@ -400,7 +397,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
clock.addDays(30);
assertListenerStatus();
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2);
ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
@@ -413,7 +410,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.MONTHLY);
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 3);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -430,7 +427,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
clock.addMonths(1);
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 4);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -452,7 +449,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
clock.addMonths(1);
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 5);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -480,23 +477,22 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
@Test(groups = "slow")
public void testRepairWithFullItemAdjustment() throws Exception {
-
final LocalDate today = new LocalDate(2013, 7, 19);
- final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
-
// Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
clock.setDeltaFromReality(today.toDateTimeAtCurrentTime(DateTimeZone.UTC).getMillis() - clock.getUTCNow().getMillis());
+ final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
final String productName = "Shotgun";
final BillingPeriod term = BillingPeriod.ANNUAL;
final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+ assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.ANNUAL);
@@ -505,7 +501,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
clock.addDays(30);
assertListenerStatus();
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2);
ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
new ExpectedInvoiceItemCheck(new LocalDate(2013, 8, 18), new LocalDate(2014, 8, 18), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
@@ -517,7 +513,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
bpEntitlement.cancelEntitlementWithPolicyOverrideBillingPolicy(EntitlementActionPolicy.IMMEDIATE, BillingActionPolicy.IMMEDIATE, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 3);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
new ExpectedInvoiceItemCheck(new LocalDate(2013, 8, 18), new LocalDate(2014, 8, 18), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
@@ -551,23 +547,22 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
//
@Test(groups = "slow")
public void testRepairWithPartialItemAdjustment() throws Exception {
-
final LocalDate today = new LocalDate(2013, 7, 19);
- final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
-
// Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
clock.setDeltaFromReality(today.toDateTimeAtCurrentTime(DateTimeZone.UTC).getMillis() - clock.getUTCNow().getMillis());
+ final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
final String productName = "Shotgun";
final BillingPeriod term = BillingPeriod.ANNUAL;
final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+ assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.ANNUAL);
@@ -576,7 +571,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
clock.addDays(30);
assertListenerStatus();
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2);
ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
new ExpectedInvoiceItemCheck(new LocalDate(2013, 8, 18), new LocalDate(2014, 8, 18), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
@@ -588,7 +583,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
bpEntitlement.cancelEntitlementWithPolicyOverrideBillingPolicy(EntitlementActionPolicy.IMMEDIATE, BillingActionPolicy.IMMEDIATE, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 3);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
new ExpectedInvoiceItemCheck(new LocalDate(2013, 8, 18), new LocalDate(2014, 8, 18), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
index 403343f..729e277 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
@@ -73,30 +73,30 @@ public class TestIntegrationWithAutoInvoiceOffTag extends TestIntegrationBase {
add_AUTO_INVOICING_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
// set next invoice to fail and create network
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK);
assertNotNull(bpEntitlement);
- Collection<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+ Collection<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 0);
clock.addDays(10); // DAY 10 still in trial
assertListenerStatus();
- invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 0);
busHandler.pushExpectedEvents(NextEvent.PHASE);
clock.addDays(30); // DAY 40 out of trial
assertListenerStatus();
- invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 0);
busHandler.pushExpectedEvents(NextEvent.TAG, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
remove_AUTO_INVOICING_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
assertListenerStatus();
- invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 1);
}
@@ -105,10 +105,10 @@ public class TestIntegrationWithAutoInvoiceOffTag extends TestIntegrationBase {
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
// set next invoice to fail and create network
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- Collection<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+ Collection<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 1); // first invoice is generated immediately after creation can't reliably stop it
add_AUTO_INVOICING_OFF_Tag(bpEntitlement.getSubscriptionBase().getBundleId(), ObjectType.BUNDLE);
@@ -117,7 +117,7 @@ public class TestIntegrationWithAutoInvoiceOffTag extends TestIntegrationBase {
clock.addDays(40); // DAY 40 out of trial
assertListenerStatus();
- invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 1); //No additional invoices generated
}
@@ -126,22 +126,22 @@ public class TestIntegrationWithAutoInvoiceOffTag extends TestIntegrationBase {
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
// set next invoice to fail and create network
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- final DefaultEntitlement bpEntitlement2 = createBaseEntitlementAndCheckForCompletion(account.getId(), "whatever", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement2 = createBaseEntitlementAndCheckForCompletion(account.getId(), "whatever", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement2);
- Collection<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+ Collection<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2); // first invoice is generated immediately after creation can't reliably stop it
add_AUTO_INVOICING_OFF_Tag(bpEntitlement.getSubscriptionBase().getBundleId(), ObjectType.BUNDLE);
- busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(40); // DAY 40 out of trial
assertListenerStatus();
- invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 3); // Only one additional invoice generated
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java
index 2338747..73ee3cd 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java
@@ -69,10 +69,10 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
add_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- Collection<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ Collection<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 1);
busHandler.pushExpectedEvents(NextEvent.PHASE);
@@ -81,7 +81,7 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2);
for (Invoice cur : invoices) {
if (cur.getChargedAmount().compareTo(BigDecimal.ZERO) == 0) {
@@ -93,7 +93,7 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
remove_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
addDelayBceauseOfLackOfCorrectSynchro();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2);
for (Invoice cur : invoices) {
if (cur.getChargedAmount().compareTo(BigDecimal.ZERO) == 0) {
@@ -110,10 +110,10 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
add_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- Collection<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ Collection<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 1);
busHandler.pushExpectedEvents(NextEvent.PHASE);
@@ -122,7 +122,7 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2);
for (Invoice cur : invoices) {
if (cur.getChargedAmount().compareTo(BigDecimal.ZERO) == 0) {
@@ -135,7 +135,7 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
remove_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
addDelayBceauseOfLackOfCorrectSynchro();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2);
for (Invoice cur : invoices) {
if (cur.getChargedAmount().compareTo(BigDecimal.ZERO) == 0) {
@@ -152,7 +152,7 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
clock.addDays(nbDaysBeforeRetry + 1);
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
for (Invoice cur : invoices) {
if (cur.getChargedAmount().compareTo(BigDecimal.ZERO) == 0) {
continue;
@@ -169,10 +169,10 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
add_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- Collection<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ Collection<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 1);
// CREATE FIRST NON NULL INVOICE + FIRST PAYMENT/ATTEMPT -> AUTO_PAY_OFF
@@ -181,7 +181,7 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
clock.addDays(31); // After trial
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2);
for (Invoice cur : invoices) {
if (cur.getChargedAmount().compareTo(BigDecimal.ZERO) == 0) {
@@ -195,7 +195,7 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
remove_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
addDelayBceauseOfLackOfCorrectSynchro();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2);
for (Invoice cur : invoices) {
if (cur.getChargedAmount().compareTo(BigDecimal.ZERO) == 0) {
@@ -213,7 +213,7 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
clock.addDays(nbDaysBeforeRetry + 1);
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2);
for (Invoice cur : invoices) {
if (cur.getChargedAmount().compareTo(BigDecimal.ZERO) == 0) {
@@ -229,7 +229,7 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
remove_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
addDelayBceauseOfLackOfCorrectSynchro();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
for (Invoice cur : invoices) {
if (cur.getChargedAmount().compareTo(BigDecimal.ZERO) == 0) {
continue;
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithDifferentBillingPeriods.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithDifferentBillingPeriods.java
index 55c5715..3284c5b 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithDifferentBillingPeriods.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithDifferentBillingPeriods.java
@@ -58,12 +58,12 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
final String productName = "Shotgun";
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+ assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.MONTHLY);
@@ -72,7 +72,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
clock.addDays(31);
assertListenerStatus();
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2);
ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
@@ -85,7 +85,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
changeEntitlementAndCheckForCompletion(bpEntitlement, productName, BillingPeriod.ANNUAL, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 3);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -113,12 +113,12 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
final String productName = "Pistol";
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+ assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.MONTHLY);
@@ -127,7 +127,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
clock.addDays(31);
assertListenerStatus();
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2);
ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
@@ -140,7 +140,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
changeEntitlementAndCheckForCompletion(bpEntitlement, productName, BillingPeriod.QUARTERLY, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 3);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -153,12 +153,12 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, toBeChecked);
// Move to 1020-08-01
- busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(20);
clock.addMonths(2);
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 4);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -182,12 +182,12 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
final String productName = "Shotgun";
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+ assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.ANNUAL);
@@ -199,11 +199,11 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
// 2012-5-12
clock.addDays(10);
- busHandler.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK, NextEvent.INVOICE);
+ busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.INVOICE);
entitlementApi.pause(bpEntitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 3);
ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -218,11 +218,11 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
// 2012-6-4
clock.addDays(23);
- busHandler.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
entitlementApi.resume(bpEntitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 4);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -230,11 +230,11 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 4), new LocalDate(2012, 6, 4), InvoiceItemType.CBA_ADJ, new BigDecimal("-2327.62")));
invoiceChecker.checkInvoice(invoices.get(3).getId(), callContext, toBeChecked);
- busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addYears(1);
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 5);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -257,12 +257,12 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
final String productName = "Shotgun";
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+ assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.ANNUAL);
@@ -279,13 +279,13 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
// 2012-5-12
clock.addDays(10);
- busHandler.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK);
+ busHandler.pushExpectedEvents(NextEvent.BLOCK);
entitlementApi.pause(bpEntitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
// 2012-6-4
clock.addDays(23);
- busHandler.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK);
+ busHandler.pushExpectedEvents(NextEvent.BLOCK);
entitlementApi.resume(bpEntitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
@@ -294,7 +294,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
tagUserApi.removeTag(account.getId(), ObjectType.ACCOUNT, ControlTagType.AUTO_INVOICING_OFF.getId(), callContext);
assertListenerStatus();
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 3);
@@ -307,11 +307,11 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 12), new LocalDate(2013, 5, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-2327.62")));
invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, toBeChecked);
- busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addYears(1);
assertListenerStatus();
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 4);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -337,9 +337,9 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
clock.setDeltaFromReality(startDate.toDateTimeAtCurrentTime(DateTimeZone.UTC).getMillis() - clock.getUTCNow().getMillis());
// Create subscription and check we get the initial invoice for the 30 days TRIAL
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+ assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.ANNUAL);
// Move out of TRIAL and verify we invioice for a full year
@@ -347,7 +347,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
clock.addDays(30);
assertListenerStatus();
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2);
ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
new ExpectedInvoiceItemCheck(new LocalDate(2015, 1, 1), new LocalDate(2016, 1, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
@@ -363,7 +363,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
clock.addDays(73);
changeEntitlementAndCheckForCompletion(bpEntitlement, "Assault-Rifle", BillingPeriod.ANNUAL, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 3);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
new ExpectedInvoiceItemCheck(new LocalDate(2015, 3, 15), new LocalDate(2016, 3, 1), InvoiceItemType.RECURRING, new BigDecimal("5770.44")),
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithWrittenOffTag.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithWrittenOffTag.java
index f5d854c..013ad68 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithWrittenOffTag.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithWrittenOffTag.java
@@ -77,17 +77,17 @@ public class TestIntegrationWithWrittenOffTag extends TestIntegrationBase {
add_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
// set next invoice to fail and create network
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- List<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+ List<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 1);
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE);
clock.addDays(31);
assertListenerStatus();
- invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2);
// Tag non $0 invoice with WRITTEN_OFF and remove AUTO_PAY_OFF => System should still not pay anything
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoiceNotifications.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoiceNotifications.java
index 481952d..c51b21e 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoiceNotifications.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoiceNotifications.java
@@ -41,16 +41,15 @@ public class TestInvoiceNotifications extends TestIntegrationBase {
@Test(groups = "slow")
public void testInvoiceNotificationBasic() throws Exception {
+ // We take april as it has 30 days (easier to play with BCD)
+ // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+ clock.setDay(new LocalDate(2012, 4, 1));
final AccountData accountData = getAccountData(1);
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
accountChecker.checkAccount(account.getId(), accountData, callContext);
- // We take april as it has 30 days (easier to play with BCD)
- // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
- clock.setDay(new LocalDate(2012, 4, 1));
-
- final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
// Move to end of trial => 2012, 4, 24
addDaysAndCheckForCompletion(23, NextEvent.INVOICE_NOTIFICATION);
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java
index 641b61d..6d84c20 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java
@@ -18,13 +18,19 @@
package org.killbill.billing.beatrix.integration;
import java.math.BigDecimal;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.killbill.billing.ObjectType;
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.beatrix.util.PaymentChecker.ExpectedPaymentCheck;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.catalog.api.ProductCategory;
@@ -32,9 +38,12 @@ import org.killbill.billing.entitlement.api.DefaultEntitlement;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.invoice.api.InvoicePaymentType;
import org.killbill.billing.invoice.model.ExternalChargeInvoiceItem;
import org.killbill.billing.payment.api.Payment;
import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.payment.api.TransactionStatus;
+import org.testng.Assert;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
@@ -46,8 +55,281 @@ import static org.testng.Assert.assertTrue;
public class TestInvoicePayment extends TestIntegrationBase {
@Test(groups = "slow")
- public void testPartialPayments() throws Exception {
+ public void testPartialPaymentByPaymentPlugin() throws Exception {
+ // 2012-05-01T00:03:42.000Z
+ clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+ final AccountData accountData = getAccountData(0);
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+ invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+ invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
+
+ // Trigger a partial payment on the next invoice
+ paymentPlugin.overrideNextProcessedAmount(BigDecimal.TEN);
+
+ // 2012-05-31 => DAY 30 have to get out of trial {I0, P0}
+ addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+ Invoice invoice2 = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+ invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
+
+ // Invoice is partially paid
+ final Payment payment1 = paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 5, 31), new BigDecimal("249.95"), TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("239.95")), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+ // 2012-06-30
+ addDaysAndCheckForCompletion(30, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+ Invoice invoice3 = invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+ invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 7, 31), callContext);
+
+ // Invoice is fully paid
+ final Payment payment2 = paymentChecker.checkPayment(account.getId(), 2, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 6, 30), new BigDecimal("249.95"), TransactionStatus.SUCCESS, invoice3.getId(), Currency.USD));
+ Assert.assertEquals(payment2.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment2.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("249.95")), 0);
+ invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+ Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("239.95")), 0);
+ Assert.assertEquals(invoice3.getBalance().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+ // Fully pay the second invoice
+ final Payment payment3 = createPaymentAndCheckForCompletion(account, invoice2, invoice2.getBalance(), account.getCurrency(), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ paymentChecker.checkPayment(account.getId(), 3, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 6, 30), new BigDecimal("239.95"), TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
+ Assert.assertEquals(payment3.getPurchasedAmount().compareTo(new BigDecimal("239.95")), 0);
+ Assert.assertEquals(payment3.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("239.95")), 0);
+ invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+ invoice3 = invoiceUserApi.getInvoice(invoice3.getId(), callContext);
+ Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(invoice3.getBalance().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(BigDecimal.ZERO), 0);
+ }
+
+ @Test(groups = "slow")
+ public void testPartialRefunds() throws Exception {
+ // 2012-05-01T00:03:42.000Z
+ clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+
+ final AccountData accountData = getAccountData(0);
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+ invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+ invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
+
+ // 2012-05-31 => DAY 30 have to get out of trial {I0, P0}
+ addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+ Invoice invoice2 = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+ invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
+
+ // Invoice is fully paid
+ Payment payment1 = paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 5, 31), new BigDecimal("249.95"), TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getRefundedAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+ // Trigger first partial refund ($1), no adjustment
+ payment1 = refundPaymentAndCheckForCompletion(account, payment1, BigDecimal.TEN, Currency.USD, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getRefundedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().size(), 2);
+ Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(1).getAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().get(1).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+ invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+ Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+ // Trigger second partial refund ($1), with item adjustment
+ final Map<UUID, BigDecimal> iias = new HashMap<UUID, BigDecimal>();
+ iias.put(invoice2.getInvoiceItems().get(0).getId(), BigDecimal.ONE);
+ payment1 = refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment1, BigDecimal.ONE, Currency.USD, iias, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.INVOICE_ADJUSTMENT);
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getRefundedAmount().compareTo(new BigDecimal("11")), 0);
+ Assert.assertEquals(payment1.getTransactions().size(), 3);
+ Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(1).getAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().get(1).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().get(2).getAmount().compareTo(BigDecimal.ONE), 0);
+ Assert.assertEquals(payment1.getTransactions().get(2).getProcessedAmount().compareTo(BigDecimal.ONE), 0);
+ invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+ Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+ // Trigger third partial refund ($10), with item adjustment
+ iias.put(invoice2.getInvoiceItems().get(0).getId(), BigDecimal.TEN);
+ payment1 = refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment1, BigDecimal.TEN, Currency.USD, iias, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.INVOICE_ADJUSTMENT);
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getRefundedAmount().compareTo(new BigDecimal("21")), 0);
+ Assert.assertEquals(payment1.getTransactions().size(), 4);
+ Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(1).getAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().get(1).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().get(2).getAmount().compareTo(BigDecimal.ONE), 0);
+ Assert.assertEquals(payment1.getTransactions().get(2).getProcessedAmount().compareTo(BigDecimal.ONE), 0);
+ Assert.assertEquals(payment1.getTransactions().get(3).getAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().get(3).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+ invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+ Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+ }
+
+ @Test(groups = "slow")
+ public void testPartialPaymentByPaymentPluginThenChargeback() throws Exception {
+ // 2012-05-01T00:03:42.000Z
+ clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+
+ final AccountData accountData = getAccountData(0);
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+ invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+ invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
+
+ // Trigger a partial payment on the next invoice
+ paymentPlugin.overrideNextProcessedAmount(BigDecimal.TEN);
+
+ // 2012-05-31 => DAY 30 have to get out of trial {I0, P0}
+ addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+ Invoice invoice2 = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+ invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
+
+ // Invoice is partially paid
+ Payment payment1 = paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 5, 31), new BigDecimal("249.95"), TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().size(), 1);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("239.95")), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+ // Trigger chargeback
+ payment1 = createChargeBackAndCheckForCompletion(account, payment1, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().size(), 2);
+ Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().get(1).getAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().get(1).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+ invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+ Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+ }
+
+ @Test(groups = "slow")
+ public void testAUTO_PAY_OFFThenPartialPayment() throws Exception {
+ // 2012-05-01T00:03:42.000Z
+ clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+
+ final AccountData accountData = getAccountData(0);
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+ invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+ invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
+
+ // Put the account in AUTO_PAY_OFF to make sure payment system does not try to pay the initial invoice
+ add_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
+
+ // 2012-05-31 => DAY 30 have to get out of trial {I0, P0}
+ addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE);
+
+ Invoice invoice2 = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+ invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
+
+ // Invoice is not paid
+ Assert.assertEquals(paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext).size(), 0);
+ Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+ // Trigger partial payment
+ final Payment payment1 = createPaymentAndCheckForCompletion(account, invoice2, BigDecimal.TEN, account.getCurrency(), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 5, 31), BigDecimal.TEN, TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
+ Assert.assertEquals(payment1.getTransactions().size(), 1);
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+ invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+ Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("239.95")), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+ // Remove AUTO_PAY_OFF and verify the invoice is fully paid
+ remove_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ final Payment payment2 = paymentChecker.checkPayment(account.getId(), 2, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 5, 31), new BigDecimal("239.95"), TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
+ Assert.assertEquals(payment2.getTransactions().size(), 1);
+ Assert.assertEquals(payment2.getPurchasedAmount().compareTo(new BigDecimal("239.95")), 0);
+ Assert.assertEquals(payment2.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("239.95")), 0);
+ invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+ Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+ }
+
+ @Test(groups = "slow")
+ public void testPaymentDifferentCurrencyByPaymentPlugin() throws Exception {
+ // 2012-05-01T00:03:42.000Z
+ clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+
+ final AccountData accountData = getAccountData(0);
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+ invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+ invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
+
+ // Trigger a payment on the next invoice with a different currency ($249.95 <-> 225.44€)
+ paymentPlugin.overrideNextProcessedAmount(new BigDecimal("225.44"));
+ paymentPlugin.overrideNextProcessedCurrency(Currency.EUR);
+
+ // 2012-05-31 => DAY 30 have to get out of trial {I0, P0}
+ addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+ Invoice invoice2 = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+ invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
+
+ // Invoice is fully paid
+ Payment payment1 = paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 5, 31), new BigDecimal("249.95"), TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().size(), 1);
+ Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(0).getCurrency(), Currency.USD);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("225.44")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedCurrency(), Currency.EUR);
+ Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(BigDecimal.ZERO), 0);
+
+ // Trigger chargeback in the original currency
+ payment1 = createChargeBackAndCheckForCompletion(account, payment1, new BigDecimal("225.44"), Currency.EUR, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().size(), 2);
+ Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(0).getCurrency(), Currency.USD);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("225.44")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedCurrency(), Currency.EUR);
+ Assert.assertEquals(payment1.getTransactions().get(1).getAmount().compareTo(new BigDecimal("225.44")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(1).getCurrency(), Currency.EUR);
+ Assert.assertEquals(payment1.getTransactions().get(1).getProcessedAmount().compareTo(new BigDecimal("225.44")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(1).getProcessedCurrency(), Currency.EUR);
+ invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+ Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+ }
+
+ @Test(groups = "slow")
+ public void testPartialRefundsOnPartialPayments() throws Exception {
final AccountData accountData = getAccountData(1);
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
accountChecker.checkAccount(account.getId(), accountData, callContext);
@@ -61,8 +343,9 @@ public class TestInvoicePayment extends TestIntegrationBase {
final InvoiceItem item1 = invoiceUserApi.insertExternalCharges(account.getId(), clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), true, callContext).get(0);
assertListenerStatus();
+ // Trigger first partial payment ($4) on first invoice
final Invoice invoice = invoiceUserApi.getInvoice(item1.getInvoiceId(), callContext);
- final Payment payment1 = createPaymentAndCheckForCompletion(account, invoice, new BigDecimal("4.00"), account.getCurrency(), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ Payment payment1 = createPaymentAndCheckForCompletion(account, invoice, new BigDecimal("4.00"), account.getCurrency(), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
Invoice invoice1 = invoiceUserApi.getInvoice(item1.getInvoiceId(), callContext);
assertTrue(invoice1.getBalance().compareTo(new BigDecimal("6.00")) == 0);
@@ -72,7 +355,8 @@ public class TestInvoicePayment extends TestIntegrationBase {
BigDecimal accountBalance = invoiceUserApi.getAccountBalance(account.getId(), callContext);
assertTrue(accountBalance.compareTo(new BigDecimal("6.00")) == 0);
- final Payment payment2 = createPaymentAndCheckForCompletion(account, invoice, new BigDecimal("6.00"), account.getCurrency(), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ // Trigger second partial payment ($6) on first invoice
+ Payment payment2 = createPaymentAndCheckForCompletion(account, invoice, new BigDecimal("6.00"), account.getCurrency(), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
invoice1 = invoiceUserApi.getInvoice(item1.getInvoiceId(), callContext);
assertTrue(invoice1.getBalance().compareTo(BigDecimal.ZERO) == 0);
@@ -82,35 +366,60 @@ public class TestInvoicePayment extends TestIntegrationBase {
accountBalance = invoiceUserApi.getAccountBalance(account.getId(), callContext);
assertTrue(accountBalance.compareTo(BigDecimal.ZERO) == 0);
-/*
- This does not work since item is paid across multiple payments and so the mount is bigger than the payment.
-
- // Now, issue refund with item adjustment on first invoice/item
- paymentApi.createRefundWithItemsAdjustments(account, payment1.getId(), Sets.<UUID>newHashSet(item1.getId()), callContext);
-
+ // Refund first payment with item adjustment
+ final Map<UUID, BigDecimal> iias = new HashMap<UUID, BigDecimal>();
+ iias.put(item1.getId(), new BigDecimal("4.00"));
+ payment1 = refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment1, new BigDecimal("4.00"), Currency.USD, iias, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.INVOICE_ADJUSTMENT);
invoice1 = invoiceUserApi.getInvoice(item1.getInvoiceId(), callContext);
assertTrue(invoice1.getBalance().compareTo(BigDecimal.ZERO) == 0);
-
accountBalance = invoiceUserApi.getAccountBalance(account.getId(), callContext);
assertTrue(accountBalance.compareTo(BigDecimal.ZERO) == 0);
-*/
-
- refundPaymentAndCheckForCompletion(account, payment1, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
-
+ // Refund second payment with item adjustment
+ iias.put(item1.getId(), new BigDecimal("6.00"));
+ payment2 = refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment2, new BigDecimal("6.00"), Currency.USD, iias, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.INVOICE_ADJUSTMENT);
invoice1 = invoiceUserApi.getInvoice(item1.getInvoiceId(), callContext);
- assertTrue(invoice1.getBalance().compareTo(new BigDecimal("4.00")) == 0);
-
+ assertTrue(invoice1.getBalance().compareTo(BigDecimal.ZERO) == 0);
accountBalance = invoiceUserApi.getAccountBalance(account.getId(), callContext);
- assertTrue(accountBalance.compareTo(new BigDecimal("4.00")) == 0);
+ assertTrue(accountBalance.compareTo(BigDecimal.ZERO) == 0);
+ Assert.assertEquals(invoice1.getPayments().size(), 4);
+
+ // Verify links for payment 1
+ Assert.assertEquals(invoice1.getPayments().get(0).getAmount().compareTo(new BigDecimal("4.00")), 0);
+ Assert.assertNull(invoice1.getPayments().get(0).getLinkedInvoicePaymentId());
+ Assert.assertNull(invoice1.getPayments().get(0).getPaymentCookieId());
+ Assert.assertEquals(invoice1.getPayments().get(0).getPaymentId(), payment1.getId());
+ Assert.assertEquals(invoice1.getPayments().get(0).getType(), InvoicePaymentType.ATTEMPT);
+ Assert.assertTrue(invoice1.getPayments().get(0).isSuccess());
+
+ // Verify links for payment 2
+ Assert.assertEquals(invoice1.getPayments().get(1).getAmount().compareTo(new BigDecimal("6.00")), 0);
+ Assert.assertNull(invoice1.getPayments().get(1).getLinkedInvoicePaymentId());
+ Assert.assertNull(invoice1.getPayments().get(1).getPaymentCookieId());
+ Assert.assertEquals(invoice1.getPayments().get(1).getPaymentId(), payment2.getId());
+ Assert.assertEquals(invoice1.getPayments().get(1).getType(), InvoicePaymentType.ATTEMPT);
+ Assert.assertTrue(invoice1.getPayments().get(1).isSuccess());
+
+ // Verify links for refund 1
+ Assert.assertEquals(invoice1.getPayments().get(2).getAmount().compareTo(new BigDecimal("-4.00")), 0);
+ Assert.assertEquals(invoice1.getPayments().get(2).getLinkedInvoicePaymentId(), invoice1.getPayments().get(0).getId());
+ Assert.assertEquals(invoice1.getPayments().get(2).getPaymentCookieId(), payment1.getTransactions().get(1).getExternalKey());
+ Assert.assertEquals(invoice1.getPayments().get(2).getPaymentId(), payment1.getId());
+ Assert.assertEquals(invoice1.getPayments().get(2).getType(), InvoicePaymentType.REFUND);
+ Assert.assertTrue(invoice1.getPayments().get(2).isSuccess());
+
+ // Verify links for refund 2
+ Assert.assertEquals(invoice1.getPayments().get(3).getAmount().compareTo(new BigDecimal("-6.00")), 0);
+ Assert.assertEquals(invoice1.getPayments().get(3).getLinkedInvoicePaymentId(), invoice1.getPayments().get(1).getId());
+ Assert.assertEquals(invoice1.getPayments().get(3).getPaymentCookieId(), payment2.getTransactions().get(1).getExternalKey());
+ Assert.assertEquals(invoice1.getPayments().get(3).getPaymentId(), payment2.getId());
+ Assert.assertEquals(invoice1.getPayments().get(3).getType(), InvoicePaymentType.REFUND);
+ Assert.assertTrue(invoice1.getPayments().get(3).isSuccess());
}
- //
-
@Test(groups = "slow")
public void testWithPaymentFailure() throws Exception {
-
clock.setDay(new LocalDate(2012, 4, 1));
final AccountData accountData = getAccountData(1);
@@ -119,44 +428,58 @@ public class TestInvoicePayment extends TestIntegrationBase {
paymentPlugin.makeNextPaymentFailWithError();
- createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
clock.addDays(30);
assertListenerStatus();
- final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2);
final Invoice invoice1 = invoices.get(0).getInvoiceItems().get(0).getInvoiceItemType() == InvoiceItemType.RECURRING ?
- invoices.get(0) : invoices.get(1);
+ invoices.get(0) : invoices.get(1);
assertTrue(invoice1.getBalance().compareTo(new BigDecimal("249.95")) == 0);
assertTrue(invoice1.getPaidAmount().compareTo(BigDecimal.ZERO) == 0);
assertTrue(invoice1.getChargedAmount().compareTo(new BigDecimal("249.95")) == 0);
assertEquals(invoice1.getPayments().size(), 1);
+ assertEquals(invoice1.getPayments().get(0).getAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(invoice1.getPayments().get(0).getCurrency(), Currency.USD);
assertFalse(invoice1.getPayments().get(0).isSuccess());
- BigDecimal accountBalance1 = invoiceUserApi.getAccountBalance(account.getId(), callContext);
+ final BigDecimal accountBalance1 = invoiceUserApi.getAccountBalance(account.getId(), callContext);
assertTrue(accountBalance1.compareTo(new BigDecimal("249.95")) == 0);
final List<Payment> payments = paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
assertEquals(payments.size(), 1);
+ assertEquals(payments.get(0).getPurchasedAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payments.get(0).getTransactions().size(), 1);
+ assertEquals(payments.get(0).getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+ assertEquals(payments.get(0).getTransactions().get(0).getCurrency(), Currency.USD);
+ assertEquals(payments.get(0).getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payments.get(0).getTransactions().get(0).getProcessedCurrency(), Currency.USD);
// Trigger the payment retry
busHandler.pushExpectedEvents(NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(8);
assertListenerStatus();
- Invoice invoice2 = invoiceUserApi.getInvoice(invoice1.getId(), callContext);
+ final Invoice invoice2 = invoiceUserApi.getInvoice(invoice1.getId(), callContext);
assertTrue(invoice2.getBalance().compareTo(BigDecimal.ZERO) == 0);
assertTrue(invoice2.getPaidAmount().compareTo(new BigDecimal("249.95")) == 0);
assertTrue(invoice2.getChargedAmount().compareTo(new BigDecimal("249.95")) == 0);
assertEquals(invoice2.getPayments().size(), 1);
assertTrue(invoice2.getPayments().get(0).isSuccess());
- BigDecimal accountBalance2 = invoiceUserApi.getAccountBalance(account.getId(), callContext);
+ final BigDecimal accountBalance2 = invoiceUserApi.getAccountBalance(account.getId(), callContext);
assertTrue(accountBalance2.compareTo(BigDecimal.ZERO) == 0);
- }
-
+ final List<Payment> payments2 = paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+ assertEquals(payments2.size(), 1);
+ assertEquals(payments2.get(0).getTransactions().size(), 2);
+ assertEquals(payments2.get(0).getTransactions().get(1).getAmount().compareTo(new BigDecimal("249.95")), 0);
+ assertEquals(payments2.get(0).getTransactions().get(1).getCurrency(), Currency.USD);
+ assertEquals(payments2.get(0).getTransactions().get(1).getProcessedAmount().compareTo(new BigDecimal("249.95")), 0);
+ assertEquals(payments2.get(0).getTransactions().get(1).getProcessedCurrency(), Currency.USD);
+ }
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestMigrationSubscriptions.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestMigrationSubscriptions.java
new file mode 100644
index 0000000..cdd69e5
--- /dev/null
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestMigrationSubscriptions.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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 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.PhaseType;
+import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.PriceListSet;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.api.DefaultEntitlementSpecifier;
+import org.killbill.billing.entitlement.api.Entitlement;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
+import org.killbill.billing.entitlement.api.EntitlementSpecifier;
+import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+//
+// These scenarios emulate commons migrations problems (they go on verifying proper entitlement startDate, and proper billing startDate along with invoices, ..)
+//
+// Note that all events associated to entitlement are called 'BLOCK' because of the underlying `BlockingState` mechanism used to implement those
+// (See http://killbill.io/blog/blockingstate-abstractions/)
+//
+public class TestMigrationSubscriptions extends TestIntegrationBase {
+
+ //
+ // Scenario: On 2016-1-1, we decide to migrate a subscription with a cutOverDate of 2016-1-10 (10 days in the future) and a billing date of 2016-1-31
+ // (note that 2016-1-31 + 30 days trial = 2016-03-01, which aligns well with the BCD=1 we set on our test account)
+ //
+ //
+ @Test(groups = "slow")
+ public void testSimpleMigrationBP() throws Exception {
+
+ clock.setDay(new LocalDate(2016, 1, 1));
+
+ final AccountData accountData = getAccountData(1);
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+
+ // We set both entitlement and billing date with desired value
+ final LocalDate entitlementMigrationDate = new LocalDate(2016, 1, 10);
+ final LocalDate billingMigrationDate = new LocalDate(2016, 1, 31);
+
+ // Entitlement wil be created in PENDING state
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "bundleKey", null, entitlementMigrationDate, billingMigrationDate, false, ImmutableList.<PluginProperty>of(), callContext);
+ Assert.assertEquals(entitlement.getState(), EntitlementState.PENDING);
+
+ // Move clock to entitlementMigrationDate (migration cutOverDate), and expect the associated event
+ busHandler.pushExpectedEvents(NextEvent.BLOCK);
+ clock.addDays(10);
+ assertListenerStatus();
+
+ final Entitlement activeEntitlement = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
+ Assert.assertEquals(activeEntitlement.getState(), EntitlementState.ACTIVE);
+
+ // Move clock to billingMigrationDate and expect the CREATE event along a $0 invoice for the trial
+ busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.INVOICE);
+ clock.addDays(21);
+ assertListenerStatus();
+
+ expectedInvoices.add(new ExpectedInvoiceItemCheck(billingMigrationDate, null, InvoiceItemType.FIXED, new BigDecimal("0")));
+ invoiceChecker.checkInvoice(account.getId(), 1, callContext, expectedInvoices);
+ expectedInvoices.clear();
+
+ // Move clock next month for first RECURRING invoice
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ clock.addMonths(1);
+ assertListenerStatus();
+
+ final LocalDate startDate = billingMigrationDate.plusDays(30);
+ final LocalDate endDate = startDate.plusMonths(1);
+ expectedInvoices.add(new ExpectedInvoiceItemCheck(startDate, endDate, InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+ invoiceChecker.checkInvoice(account.getId(), 2, callContext, expectedInvoices);
+ expectedInvoices.clear();
+ }
+
+ //
+ // Scenario: On 2016-1-1, we decide to migrate a subscription with a cutOverDate of 2015-12-20 (12 days in the past) and a billing date of 2016-2-01, we we want to skip the trial
+ // (note that since we skip the trial billingDate = 2016-2-01 aligns well with the BCD=1 we set on our test account)
+ //
+ //
+ @Test(groups = "slow")
+ public void testSimpleMigrationBPSkipTrial() throws Exception {
+
+ clock.setDay(new LocalDate(2016, 1, 1));
+
+ final AccountData accountData = getAccountData(1);
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+
+ // We set both entitlement and billing date with desired value
+ final LocalDate entitlementMigrationDate = new LocalDate(2015, 12, 20);
+ final LocalDate billingMigrationDate = new LocalDate(2016, 2, 1);
+
+ // Entitlement wil be created in ACTIVE state because entitlementMigrationDate was set in the past
+ busHandler.pushExpectedEvents(NextEvent.BLOCK);
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "bundleKey", null, entitlementMigrationDate, billingMigrationDate, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ Assert.assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
+
+ // Move clock next month for first RECURRING invoice (note that TRIAL was correctly skipped, we directly start RECURRING on billingMigrationDate
+ busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ clock.addMonths(1);
+ assertListenerStatus();
+
+ final LocalDate startDate = billingMigrationDate;
+ final LocalDate endDate = startDate.plusMonths(1);
+ expectedInvoices.add(new ExpectedInvoiceItemCheck(startDate, endDate, InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+ invoiceChecker.checkInvoice(account.getId(), 1, callContext, expectedInvoices);
+ expectedInvoices.clear();
+
+ }
+
+ //
+ // Scenario: On 2016-1-1, we decide to migrate a subscription with a cutOverDate of 2015-12-20 (12 days in the past) and a billing date of 2016-2-01, we we want to skip the trial.
+ // In addition we subscription needs to be future cancelled (2016-2-15) at the time we migrate it
+ // (note that since we skip the trial billingDate = 2016-2-01 aligns well with the BCD=1 we set on our test account)
+ //
+ //
+ @Test(groups = "slow")
+ public void testSimpleMigrationBPSkipTrialWithPendingCancellation() throws Exception {
+
+ clock.setDay(new LocalDate(2016, 1, 1));
+
+ final AccountData accountData = getAccountData(1);
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+
+ final LocalDate entitlementMigrationDate = new LocalDate(2015, 12, 20);
+
+ // We set both entitlement and billing date with desired value
+ final LocalDate billingMigrationDate = new LocalDate(2016, 2, 1);
+ final LocalDate effectiveCancellationDate = new LocalDate(2016, 2, 15);
+
+ // Entitlement wil be created in ACTIVE state because entitlementMigrationDate was set in the past
+ busHandler.pushExpectedEvents(NextEvent.BLOCK);
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "bundleKey", null, entitlementMigrationDate, billingMigrationDate, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+ Assert.assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
+
+ // Perform the cancellation (we did not move the clock, the is is future cancellation done at the time we decide to migrate)
+ entitlement.cancelEntitlementWithDate(effectiveCancellationDate, true, ImmutableList.<PluginProperty>of(), callContext);
+
+ // Billing starts straight on EVERGREEN
+ busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ clock.addMonths(1);
+ assertListenerStatus();
+
+ // The invoice will be pro-rated up to the cancellation date
+ final LocalDate startDate = billingMigrationDate;
+ final LocalDate endDate = effectiveCancellationDate;
+ expectedInvoices.add(new ExpectedInvoiceItemCheck(startDate, endDate, InvoiceItemType.RECURRING, new BigDecimal("120.67")));
+ invoiceChecker.checkInvoice(account.getId(), 1, callContext, expectedInvoices);
+ expectedInvoices.clear();
+
+ // Move to cancellation date
+ busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
+ clock.addMonths(14);
+ assertListenerStatus();
+
+ final Entitlement cancelledEntitlement = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
+ Assert.assertEquals(cancelledEntitlement.getState(), EntitlementState.CANCELLED);
+ }
+
+
+ //
+ // Scenario: On 2016-1-1, we decide to migrate a bundle (one BP and one AO). We migrate straight to EVERGREEN phase. The scenario is very similar to previous one
+ // but with an additional AO (by using the createBaseEntitlementWithAddOns api).
+ //
+ // Note that while convenient to migrate a bundle at once (BP + AOS), one could do several calls for each subscription. The advantage of the later approach is that
+ // the granularity in terms of alignments for when things start and which phase are skipped can be better controlled.
+ //
+ @Test(groups = "slow")
+ public void testSimpleMigrationBundle() throws Exception {
+
+ clock.setDay(new LocalDate(2016, 1, 1));
+
+ final AccountData accountData = getAccountData(1);
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ // We set both entitlement and billing date with desired value
+ final LocalDate entitlementMigrationDate = new LocalDate(2015, 12, 20);
+ final LocalDate billingMigrationDate = new LocalDate(2016, 2, 1);
+
+ final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+ final PlanPhaseSpecifier addOnSpec1 = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+
+ final String externalKey = "baseExternalKey";
+ EntitlementSpecifier baseEntitlementSpecifier = new DefaultEntitlementSpecifier(baseSpec, null);
+ EntitlementSpecifier addOnEntitlementSpecifier1 = new DefaultEntitlementSpecifier(addOnSpec1, null);
+
+ final List<EntitlementSpecifier> specifierList = new ArrayList<EntitlementSpecifier>();
+ specifierList.add(baseEntitlementSpecifier);
+ specifierList.add(addOnEntitlementSpecifier1);
+
+ busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.BLOCK);
+ final Entitlement baseEntitlement = entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, entitlementMigrationDate, billingMigrationDate, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+ Assert.assertEquals(baseEntitlement.getState(), EntitlementState.ACTIVE);
+
+ // Billing starts straight on EVERGREEN
+ busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.CREATE, NextEvent.INVOICE, NextEvent.NULL_INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ clock.addMonths(1);
+ assertListenerStatus();
+ }
+
+}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentRefund.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentRefund.java
index d428c8a..cd0ef09 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentRefund.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentRefund.java
@@ -114,9 +114,9 @@ public class TestPaymentRefund extends TestIntegrationBase {
invoiceItemCount = 0;
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
invoiceChecker.checkInvoice(account.getId(), ++invoiceItemCount, callContext, new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPublicBus.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPublicBus.java
index fbe0b9e..ae8b7a3 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPublicBus.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPublicBus.java
@@ -100,6 +100,8 @@ public class TestPublicBus extends TestIntegrationBase {
log.debug("RESET TEST FRAMEWORK");
+ controllerDispatcher.clearAll();
+
overdueConfigCache.loadDefaultOverdueConfig((OverdueConfig) null);
clock.resetDeltaFromReality();
@@ -136,29 +138,36 @@ public class TestPublicBus extends TestIntegrationBase {
log.info("Beginning test with BCD of " + billingDay);
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
- final UUID accountId = account.getId();
assertNotNull(account);
// set clock to the initial start date
clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
- String productName = "Shotgun";
- BillingPeriod term = BillingPeriod.MONTHLY;
- String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
-
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
await().atMost(10, SECONDS).until(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
- // expecting ACCOUNT_CREATE, ACCOUNT_CHANGE, SUBSCRIPTION_CREATION, INVOICE_CREATION
- return externalBusCount.get() >= 4;
+ // expecting ACCOUNT_CREATE, ACCOUNT_CHANGE, SUBSCRIPTION_CREATION (2), ENTITLEMENT_CREATE INVOICE_CREATION
+ return externalBusCount.get() == 6;
}
});
+
+ addDaysAndCheckForCompletion(31, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+
+ await().atMost(10, SECONDS).until(new Callable<Boolean>() {
+ @Override
+ public Boolean call() throws Exception {
+ // 5 + SUBSCRIPTION_TRANSITION, INVOICE, PAYMENT, INVOICE_PAYMENT
+ return externalBusCount.get() == 10;
+ }
+ });
+
}
@Test(groups = "slow")
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java
index a08b05e..b174219 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java
@@ -39,6 +39,7 @@ import org.killbill.billing.entitlement.api.DefaultEntitlement;
import org.killbill.billing.entitlement.api.DefaultEntitlementSpecifier;
import org.killbill.billing.entitlement.api.Entitlement;
import org.killbill.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
import org.killbill.billing.entitlement.api.EntitlementApiException;
import org.killbill.billing.entitlement.api.EntitlementSpecifier;
import org.killbill.billing.entitlement.api.Subscription;
@@ -63,22 +64,22 @@ public class TestSubscription extends TestIntegrationBase {
public void testForcePolicy() throws Exception {
// We take april as it has 30 days (easier to play with BCD)
final LocalDate today = new LocalDate(2012, 4, 1);
- final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
-
// Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
clock.setDeltaFromReality(today.toDateTimeAtCurrentTime(DateTimeZone.UTC).getMillis() - clock.getUTCNow().getMillis());
+ final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
final String productName = "Shotgun";
final BillingPeriod term = BillingPeriod.ANNUAL;
final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+ assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.ANNUAL);
@@ -87,7 +88,7 @@ public class TestSubscription extends TestIntegrationBase {
clock.addDays(40);
assertListenerStatus();
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2);
ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
@@ -109,7 +110,7 @@ public class TestSubscription extends TestIntegrationBase {
changeEntitlementAndCheckForCompletion(bpEntitlement, productName, BillingPeriod.MONTHLY, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE);
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 3);
invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, toBeChecked);
@@ -118,7 +119,7 @@ public class TestSubscription extends TestIntegrationBase {
//
changeEntitlementAndCheckForCompletion(bpEntitlement, productName, BillingPeriod.ANNUAL, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE);
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 4);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -141,22 +142,22 @@ public class TestSubscription extends TestIntegrationBase {
public void testChangeOfPlan() throws Exception {
// We take april as it has 30 days (easier to play with BCD)
final LocalDate today = new LocalDate(2012, 4, 1);
- final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
-
// Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
clock.setDeltaFromReality(today.toDateTimeAtCurrentTime(DateTimeZone.UTC).getMillis() - clock.getUTCNow().getMillis());
+ final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
final String productName = "Shotgun";
final BillingPeriod term = BillingPeriod.ANNUAL;
final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+ assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.ANNUAL);
@@ -165,7 +166,7 @@ public class TestSubscription extends TestIntegrationBase {
clock.addDays(40);
assertListenerStatus();
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 2);
ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
@@ -176,7 +177,7 @@ public class TestSubscription extends TestIntegrationBase {
// (Note that, the catalog is configured to use CHANGE_OF_PLAN when moving to that plan and Not CHANGE_OF_PRICELIST which has not been implement;
// this is a bit misleading since we are changing pricelist, but in that case pricelist change has no effect)
changeEntitlementAndCheckForCompletion(bpEntitlement, "Assault-Rifle", BillingPeriod.ANNUAL, "rescue", BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertEquals(invoices.size(), 3);
toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
@@ -211,8 +212,8 @@ public class TestSubscription extends TestIntegrationBase {
specifierList.add(addOnEntitlementSpecifier1);
specifierList.add(addOnEntitlementSpecifier2);
- busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.CREATE, NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
- final Entitlement entitlement = entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
checkNoMoreInvoiceToGenerate(account);
@@ -234,7 +235,7 @@ public class TestSubscription extends TestIntegrationBase {
assertEquals(addOnEntitlement2.getLastActiveProduct().getName(), "Laser-Scope");
assertEquals(addOnEntitlement2.getLastActiveProductCategory(), ProductCategory.ADD_ON);
- final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
assertTrue(invoices.size() == 1); // ONLY ONE INVOICE
assertTrue(invoices.get(0).getInvoiceItems().size() == 3);
@@ -244,14 +245,10 @@ public class TestSubscription extends TestIntegrationBase {
new ExpectedInvoiceItemCheck(initialDate, null, InvoiceItemType.FIXED, new BigDecimal("0"))); // Shotgun
invoiceChecker.checkInvoice(invoices.get(0).getId(), callContext, toBeChecked);
-
}
-
-
@Test(groups = "slow")
public void testCancelFutureSubscriptionWithPolicy() throws Exception {
-
final LocalDate initialDate = new LocalDate(2015, 9, 1);
clock.setDay(initialDate);
@@ -262,7 +259,7 @@ public class TestSubscription extends TestIntegrationBase {
final LocalDate futureDate = new LocalDate(2015, 10, 1);
// No CREATE event as this is set in the future
- final Entitlement createdEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, futureDate, ImmutableList.<PluginProperty>of(), callContext);
+ final Entitlement createdEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, futureDate, futureDate, false, ImmutableList.<PluginProperty>of(), callContext);
assertEquals(createdEntitlement.getEffectiveStartDate().compareTo(futureDate), 0);
assertEquals(createdEntitlement.getEffectiveEndDate(), null);
assertListenerStatus();
@@ -273,8 +270,7 @@ public class TestSubscription extends TestIntegrationBase {
assertListenerStatus();
// Move off trial and reach start/cancellation date
- // NextEvent.INVOICE is required because of #467
- busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.CANCEL);
+ busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
clock.addDays(30);
assertListenerStatus();
@@ -285,7 +281,6 @@ public class TestSubscription extends TestIntegrationBase {
@Test(groups = "slow")
public void testCancelFutureSubscriptionWithRequestedDate() throws Exception {
-
final LocalDate initialDate = new LocalDate(2015, 9, 1);
clock.setDay(initialDate);
@@ -296,7 +291,8 @@ public class TestSubscription extends TestIntegrationBase {
final LocalDate futureDate = new LocalDate(2015, 10, 1);
// No CREATE event as this is set in the future
- final Entitlement createdEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, futureDate, ImmutableList.<PluginProperty>of(), callContext);
+ final Entitlement createdEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, futureDate, futureDate, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertEquals(createdEntitlement.getState(), EntitlementState.PENDING);
assertEquals(createdEntitlement.getEffectiveStartDate().compareTo(futureDate), 0);
assertEquals(createdEntitlement.getEffectiveEndDate(), null);
assertListenerStatus();
@@ -315,13 +311,12 @@ public class TestSubscription extends TestIntegrationBase {
assertListenerStatus();
// Move off trial and reach start/cancellation date
- busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.CANCEL);
+ busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
clock.addDays(30);
assertListenerStatus();
// Just to make sure we really don't invoice for anything move to next month
clock.addMonths(1);
assertListenerStatus();
-
}
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestTagApi.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestTagApi.java
index 80b5505..0654926 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestTagApi.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestTagApi.java
@@ -82,10 +82,10 @@ public class TestTagApi extends TestIntegrationBase {
final BillingPeriod term = BillingPeriod.ANNUAL;
final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
- final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
- final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
Assert.assertEquals(invoices.size(), 1);
final Invoice invoice = invoices.get(0);
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithCatalogPlugin.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithCatalogPlugin.java
index 7afcb20..254ea4f 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithCatalogPlugin.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithCatalogPlugin.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -100,18 +100,16 @@ public class TestWithCatalogPlugin extends TestIntegrationBase {
@Test(groups = "slow")
public void testCreateSubscriptionWithCatalogPlugin() throws Exception {
+ // We take april as it has 30 days (easier to play with BCD)
+ // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+ clock.setDay(new LocalDate(2012, 4, 1));
final AccountData accountData = getAccountData(1);
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
accountChecker.checkAccount(account.getId(), accountData, callContext);
- // We take april as it has 30 days (easier to play with BCD)
- // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
- clock.setDay(new LocalDate(2012, 4, 1));
-
- //
// Create original subscription (Trial PHASE) -> $0 invoice.
- final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithEntilementPlugin.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithEntilementPlugin.java
index 698b887..e6f7499 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithEntilementPlugin.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithEntilementPlugin.java
@@ -29,6 +29,7 @@ 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.DefaultPlanPhasePriceOverride;
+import org.killbill.billing.catalog.api.BillingActionPolicy;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
@@ -90,18 +91,16 @@ public class TestWithEntilementPlugin extends TestIntegrationBase {
testEntitlementPluginApi.setPlanPhasePriceOverride(null);
}
-
- @Test(groups = "slow")
+ @Test(groups = "slow")
public void testCreateSubscriptionWithEntitlementPlugin() throws Exception {
+ // We take april as it has 30 days (easier to play with BCD)
+ // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+ clock.setDay(new LocalDate(2012, 4, 1));
final AccountData accountData = getAccountData(1);
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
accountChecker.checkAccount(account.getId(), accountData, callContext);
- // We take april as it has 30 days (easier to play with BCD)
- // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
- clock.setDay(new LocalDate(2012, 4, 1));
-
final List<PlanPhasePriceOverride> overrides = new ArrayList<PlanPhasePriceOverride>();
overrides.add(new DefaultPlanPhasePriceOverride("shotgun-monthly-evergreen", account.getCurrency(), null, BigDecimal.TEN));
@@ -109,7 +108,7 @@ public class TestWithEntilementPlugin extends TestIntegrationBase {
//
// Create original subscription (Trial PHASE) -> $0 invoice.
- final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
@@ -146,17 +145,22 @@ public class TestWithEntilementPlugin extends TestIntegrationBase {
public boolean isAborted() {
return false;
}
-
@Override
- public LocalDate getAdjustedEffectiveDate() {
+ public LocalDate getAdjustedEntitlementEffectiveDate() {
+ return null;
+ }
+ @Override
+ public LocalDate getAdjustedBillingEffectiveDate() {
+ return null;
+ }
+ @Override
+ public BillingActionPolicy getAdjustedBillingActionPolicy() {
return null;
}
-
@Override
public List<EntitlementSpecifier> getAdjustedEntitlementSpecifiers() {
return entitlementSpecifiers;
}
-
@Override
public Iterable<PluginProperty> getAdjustedPluginProperties() {
return null;
@@ -180,5 +184,4 @@ public class TestWithEntilementPlugin extends TestIntegrationBase {
this.planPhasePriceOverride = planPhasePriceOverride;
}
}
-
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithPriceOverride.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithPriceOverride.java
index e437f17..19a1488 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithPriceOverride.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithPriceOverride.java
@@ -43,19 +43,18 @@ public class TestWithPriceOverride extends TestIntegrationBase {
@Test(groups = "slow")
public void testCreatWithFixedPriceOverride() throws Exception {
+ // We take april as it has 30 days (easier to play with BCD)
+ // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+ clock.setDay(new LocalDate(2012, 4, 1));
final AccountData accountData = getAccountData(1);
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
accountChecker.checkAccount(account.getId(), accountData, callContext);
- // We take april as it has 30 days (easier to play with BCD)
- // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
- clock.setDay(new LocalDate(2012, 4, 1));
-
final List<PlanPhasePriceOverride> overrides = new ArrayList<PlanPhasePriceOverride>();
overrides.add(new DefaultPlanPhasePriceOverride("shotgun-monthly-trial", account.getCurrency(), BigDecimal.ONE, null));
- final DefaultEntitlement bpSubscription = createBaseEntitlementWithPriceOverrideAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, overrides, NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ final DefaultEntitlement bpSubscription = createBaseEntitlementWithPriceOverrideAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, overrides, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
// 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(clock.getUTCToday(), null, InvoiceItemType.FIXED, new BigDecimal("1")));
@@ -63,19 +62,18 @@ public class TestWithPriceOverride extends TestIntegrationBase {
@Test(groups = "slow")
public void testCreateWithRecurringPriceOverride() throws Exception {
+ // We take april as it has 30 days (easier to play with BCD)
+ // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+ clock.setDay(new LocalDate(2012, 4, 1));
final AccountData accountData = getAccountData(1);
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
accountChecker.checkAccount(account.getId(), accountData, callContext);
- // We take april as it has 30 days (easier to play with BCD)
- // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
- clock.setDay(new LocalDate(2012, 4, 1));
-
final List<PlanPhasePriceOverride> overrides = new ArrayList<PlanPhasePriceOverride>();
overrides.add(new DefaultPlanPhasePriceOverride("shotgun-monthly-evergreen", account.getCurrency(), null, BigDecimal.TEN));
- final DefaultEntitlement bpSubscription = createBaseEntitlementWithPriceOverrideAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, overrides, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpSubscription = createBaseEntitlementWithPriceOverrideAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, overrides, NextEvent.CREATE, NextEvent.BLOCK, 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(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -95,16 +93,15 @@ public class TestWithPriceOverride extends TestIntegrationBase {
@Test(groups = "slow")
public void testChangePlanWithRecurringPriceOverride() throws Exception {
+ // We take april as it has 30 days (easier to play with BCD)
+ // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+ clock.setDay(new LocalDate(2012, 4, 1));
final AccountData accountData = getAccountData(1);
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
accountChecker.checkAccount(account.getId(), accountData, callContext);
- // We take april as it has 30 days (easier to play with BCD)
- // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
- clock.setDay(new LocalDate(2012, 4, 1));
-
- final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, 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(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -132,5 +129,4 @@ public class TestWithPriceOverride extends TestIntegrationBase {
invoiceChecker.checkInvoice(account.getId(), 4, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 1), new LocalDate(2012, 7, 1), InvoiceItemType.RECURRING, new BigDecimal("279.95")));
}
-
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTaxItems.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTaxItems.java
index 07726ee..c0e2582 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTaxItems.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTaxItems.java
@@ -106,18 +106,17 @@ public class TestWithTaxItems extends TestIntegrationBase {
@Test(groups = "slow")
public void testBasicTaxItems() throws Exception {
+ // We take april as it has 30 days (easier to play with BCD)
+ // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+ clock.setDay(new LocalDate(2012, 4, 1));
final AccountData accountData = getAccountData(1);
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
accountChecker.checkAccount(account.getId(), accountData, callContext);
- // We take april as it has 30 days (easier to play with BCD)
- // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
- clock.setDay(new LocalDate(2012, 4, 1));
-
//
// Create original subscription (Trial PHASE) -> $0 invoice.
- final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
@@ -128,7 +127,7 @@ public class TestWithTaxItems extends TestIntegrationBase {
assertListenerStatus();
// Add Cleaning ADD_ON => No Invoice
- busHandler.pushExpectedEvent(NextEvent.CREATE);
+ busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Cleaning", ProductCategory.ADD_ON, BillingPeriod.MONTHLY);
assertListenerStatus();
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
index 24fa051..4ef9834 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTimeZones.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTimeZones.java
@@ -24,6 +24,7 @@ import java.util.UUID;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
+import org.joda.time.IllegalInstantException;
import org.joda.time.LocalDate;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountData;
@@ -52,10 +53,8 @@ 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");
@@ -76,13 +75,13 @@ public class TestWithTimeZones extends TestIntegrationBase {
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);
+ final TestDryRunArguments dryRun = new TestDryRunArguments(DryRunType.SUBSCRIPTION_ACTION, "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, null, null,
+ SubscriptionEventType.START_BILLING, null, null, null, null);
+ final 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);
+ final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, 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);
@@ -97,7 +96,7 @@ public class TestWithTimeZones extends TestIntegrationBase {
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);
+ final LocalDate endDate = startDate.plusMonths(1);
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addMonths(1);
@@ -109,10 +108,9 @@ public class TestWithTimeZones extends TestIntegrationBase {
}
}
- // Verify cancellation logic when we exit daylight savind period
+ // Verify cancellation logic when we exit daylight saving 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.
@@ -134,36 +132,26 @@ public class TestWithTimeZones extends TestIntegrationBase {
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
accountChecker.checkAccount(account.getId(), accountData, callContext);
- busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_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);
+ Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "Something", ImmutableList.<PlanPhasePriceOverride>of(), null, null, false, 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);
+ assertListenerStatus();
// Verify first entitlement is correctly cancelled on the right date
Assert.assertEquals(entitlement.getEffectiveEndDate(), cancellationDate);
- // We move the clock to the date of the next invoice notification 2015-12-01 07:01:01 (invoice is using a fixed offset of 7 hours and all billing events are converted using that offset)
- clock.setTime(new DateTime("2015-12-01T07:01:02"));
- // Unfortunately we did not plug NullInvoice events so we cannot synchronize on that (See https://github.com/killbill/killbill/issues/448)
- assertListenerStatus();
-
- // We now move the clock to the date of the cancellation (one hour later), which match the cancellation day from the client point of view
- //
- // For curious reader, the reason why the time end up being '8:01:0' comes from (https://github.com/killbill/killbill-commons/blob/master/clock/src/main/java/org/killbill/clock/ClockUtil.java#L51):
- // We compute a DateTime in the account timezone by specifying explicitly the year-month-day we want to end up in, and shoving *a time*. The reason why we end up on an 8:01:02 is not necessarily so important,
- // What's important is that by construction that DateTime is guaranteed to match a LocalDate of 2015-12-01
- //
- busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK);
- clock.setTime(new DateTime("2015-12-01T08:01:02"));
+ // We now move the clock to the date of the cancellation, which match the cancellation day from the client point of view
+ busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE, NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
+ clock.setTime(new DateTime("2015-12-01T07:01:02Z"));
assertListenerStatus();
- // 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);
+ // Verify second that there was no repair (so the cancellation did correctly happen on the "2015-12-01")
+ final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
Assert.assertEquals(invoices.size(), 1);
}
@@ -171,7 +159,6 @@ public class TestWithTimeZones extends TestIntegrationBase {
// 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");
@@ -190,10 +177,9 @@ public class TestWithTimeZones extends TestIntegrationBase {
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
accountChecker.checkAccount(account.getId(), accountData, callContext);
- busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_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);
+ Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "Something", ImmutableList.<PlanPhasePriceOverride>of(), null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
// Cancel the next month specifying just a LocalDate
@@ -203,16 +189,69 @@ public class TestWithTimeZones extends TestIntegrationBase {
// Verify first entitlement is correctly cancelled on the right date
Assert.assertEquals(entitlement.getEffectiveEndDate(), cancellationDate);
-
// We now move the clock to the date of the cancellation which match the cancellation day from the client point of view
- busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK);
+ busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
clock.setTime(new DateTime("2015-03-01T08:01:02"));
assertListenerStatus();
// 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);
+ final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
Assert.assertEquals(invoices.size(), 1);
-
}
+ @Test(groups = "slow")
+ public void testReferenceTimeInDSTGap() throws Exception {
+ final DateTimeZone tz = DateTimeZone.forID("America/Los_Angeles");
+ clock.setTime(new DateTime(2015, 3, 7, 2, 0, 0, tz));
+
+ final AccountData accountData = new MockAccountBuilder().currency(Currency.USD)
+ .timeZone(tz)
+ .build();
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+ Assert.assertEquals(account.getTimeZone(), tz);
+ Assert.assertEquals(account.getFixedOffsetTimeZone(), DateTimeZone.forOffsetHours(-8));
+
+ // Note the gap: 2015-03-07T02:00:00.000-08:00 to 2015-03-08T03:00:00.000-07:00
+ clock.addDays(1);
+
+ try {
+ // See TimeAwareContext#toUTCDateTime (which uses account.getFixedOffsetTimeZone() instead)
+ new DateTime(clock.getUTCToday().getYear(),
+ clock.getUTCToday().getMonthOfYear(),
+ clock.getUTCToday().getDayOfMonth(),
+ account.getReferenceTime().toDateTime(tz).getHourOfDay(),
+ account.getReferenceTime().toDateTime(tz).getMinuteOfHour(),
+ account.getReferenceTime().toDateTime(tz).getSecondOfMinute(),
+ account.getTimeZone());
+ Assert.fail();
+ } catch (final IllegalInstantException e) {
+ // Illegal instant due to time zone offset transition (daylight savings time 'gap'): 2015-03-08T10:00:00.000 (America/Los_Angeles)
+ }
+
+ busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Blowdart", ProductCategory.BASE, BillingPeriod.MONTHLY, "notrial", null);
+ // Pass a date of today, to trigger TimeAwareContext#toUTCDateTime
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "Something", ImmutableList.<PlanPhasePriceOverride>of(), clock.getUTCToday(), clock.getUTCToday(), false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ Assert.assertEquals(entitlement.getEffectiveStartDate().compareTo(new LocalDate("2015-03-08")), 0);
+ Assert.assertEquals(((DefaultEntitlement) entitlement).getBasePlanSubscriptionBase().getStartDate().compareTo(new DateTime("2015-03-08T02:00:00.000-08:00")), 0);
+
+ invoiceChecker.checkChargedThroughDate(entitlement.getId(), new LocalDate("2015-04-08"), callContext);
+
+ busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ clock.addDays(31);
+ assertListenerStatus();
+
+ invoiceChecker.checkChargedThroughDate(entitlement.getId(), new LocalDate("2015-05-08"), callContext);
+
+ for (int i = 0; i < 25 ; i++) {
+ busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ clock.addMonths(1);
+ assertListenerStatus();
+
+ invoiceChecker.checkChargedThroughDate(entitlement.getId(), new LocalDate("2015-03-08").plusMonths(3 + i), callContext);
+ }
+ }
}
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 54107d1..092d2b5 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
@@ -60,19 +60,18 @@ public class TestConsumableInArrear extends TestIntegrationBase {
@Test(groups = "slow")
public void testWithNoUsageInPeriodAndOldUsage() throws Exception {
+ // We take april as it has 30 days (easier to play with BCD)
+ // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+ clock.setDay(new LocalDate(2012, 4, 1));
final AccountData accountData = getAccountData(1);
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
accountChecker.checkAccount(account.getId(), accountData, callContext);
- // We take april as it has 30 days (easier to play with BCD)
- // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
- clock.setDay(new LocalDate(2012, 4, 1));
-
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.BLOCK, 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(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -80,12 +79,12 @@ public class TestConsumableInArrear extends TestIntegrationBase {
//
// ADD ADD_ON ON THE SAME DAY
//
- final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE);
+ final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 4, 1), 99L, callContext);
setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 4, 15), 100L, callContext);
- busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(30);
assertListenerStatus();
@@ -93,9 +92,10 @@ public class TestConsumableInArrear extends TestIntegrationBase {
new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")),
new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.USAGE, new BigDecimal("5.90")));
- // We don't expect any invoice, but we want to give the system the time to verify there is nothing to do so we can fail
+ // We don't expect any invoice
+ busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE);
clock.addMonths(1);
- Thread.sleep(1000);
+ assertListenerStatus();
setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 6, 1), 50L, callContext);
setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 6, 16), 300L, callContext);
@@ -128,19 +128,18 @@ public class TestConsumableInArrear extends TestIntegrationBase {
@Test(groups = "slow")
public void testWithCancellation() throws Exception {
+ // We take april as it has 30 days (easier to play with BCD)
+ // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+ clock.setDay(new LocalDate(2012, 4, 1));
final AccountData accountData = getAccountData(1);
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
accountChecker.checkAccount(account.getId(), accountData, callContext);
- // We take april as it has 30 days (easier to play with BCD)
- // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
- clock.setDay(new LocalDate(2012, 4, 1));
-
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.BLOCK, 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(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -148,12 +147,12 @@ public class TestConsumableInArrear extends TestIntegrationBase {
//
// ADD ADD_ON ON THE SAME DAY
//
- final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE);
+ final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 4, 1), 99L, callContext);
setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 4, 15), 100L, callContext);
- busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(30);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 2, callContext,
@@ -174,14 +173,13 @@ public class TestConsumableInArrear extends TestIntegrationBase {
invoiceChecker.checkInvoice(account.getId(), 3, callContext,
new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 5, 28), InvoiceItemType.USAGE, new BigDecimal("5.90")));
+ busHandler.pushExpectedEvent(NextEvent.NULL_INVOICE);
clock.addDays(4);
- Thread.sleep(1000);
-
+ assertListenerStatus();
}
@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");
@@ -201,9 +199,9 @@ public class TestConsumableInArrear extends TestIntegrationBase {
accountChecker.checkAccount(account.getId(), accountData, callContext);
//
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
- final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+ final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.BLOCK, 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")));
@@ -212,20 +210,22 @@ public class TestConsumableInArrear extends TestIntegrationBase {
//
// ADD ADD_ON ON THE SAME DAY
//
- final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE);
+ final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
assertListenerStatus();
- busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(30);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 2, callContext,
new ExpectedInvoiceItemCheck(new LocalDate(2015, 10, 1), new LocalDate(2016, 10, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
// 2015-11-1
+ busHandler.pushExpectedEvent(NextEvent.NULL_INVOICE);
clock.addMonths(1);
assertListenerStatus();
// 2015-12-1
+ busHandler.pushExpectedEvent(NextEvent.NULL_INVOICE);
clock.addMonths(1);
assertListenerStatus();
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/util/AuditChecker.java b/beatrix/src/test/java/org/killbill/billing/beatrix/util/AuditChecker.java
index 46c8e74..fbc182f 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/util/AuditChecker.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/util/AuditChecker.java
@@ -248,7 +248,7 @@ public class AuditChecker {
private <T extends EntitySqlDao<M, E>, M extends EntityModelDao<E>, E extends Entity> M extractEntityModelFromEntityWithTargetRecordId(final UUID entityId, final UUID auditLogId, final Class<T> sqlDao, final CallContext context, final boolean useHistory) {
- final M modelDaoThatGivesMeTableName = dbi.onDemand(sqlDao).getById(entityId.toString(), callContextFactory.createInternalCallContext(context));
+ final M modelDaoThatGivesMeTableName = dbi.onDemand(sqlDao).getById(entityId.toString(), callContextFactory.createInternalCallContextWithoutAccountRecordId(context));
Integer targetRecordId = dbi.withHandle(new HandleCallback<Integer>() {
@Override
@@ -263,7 +263,7 @@ public class AuditChecker {
Long entityRecordId = nonEntityDao.retrieveHistoryTargetRecordId(Long.valueOf(targetRecordId), modelDaoThatGivesMeTableName.getHistoryTableName());
targetRecordId = new Integer(entityRecordId.intValue());
}
- return dbi.onDemand(sqlDao).getByRecordId(Long.valueOf(targetRecordId), callContextFactory.createInternalCallContext(context));
+ return dbi.onDemand(sqlDao).getByRecordId(Long.valueOf(targetRecordId), callContextFactory.createInternalCallContextWithoutAccountRecordId(context));
}
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java b/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java
index 7a10f54..8060745 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java
@@ -66,7 +66,7 @@ public class InvoiceChecker {
}
public Invoice checkInvoice(final UUID accountId, final int invoiceOrderingNumber, final CallContext context, final List<ExpectedInvoiceItemCheck> expected) throws InvoiceApiException {
- final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId, context);
+ final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId, false, context);
//Assert.assertEquals(invoices.size(), invoiceOrderingNumber);
final Invoice invoice = invoices.get(invoiceOrderingNumber - 1);
checkInvoice(invoice.getId(), context, expected);
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/util/PaymentChecker.java b/beatrix/src/test/java/org/killbill/billing/beatrix/util/PaymentChecker.java
index 69cfe82..efaf1ea 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/util/PaymentChecker.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/util/PaymentChecker.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -24,6 +24,8 @@ import java.util.UUID;
import org.joda.time.LocalDate;
import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.invoice.api.InvoicePayment;
+import org.killbill.billing.invoice.api.InvoicePaymentApi;
import org.killbill.billing.payment.api.Payment;
import org.killbill.billing.payment.api.PaymentApi;
import org.killbill.billing.payment.api.PaymentTransaction;
@@ -46,15 +48,17 @@ public class PaymentChecker {
private static final Logger log = LoggerFactory.getLogger(PaymentChecker.class);
private final PaymentApi paymentApi;
+ private final InvoicePaymentApi invoicePaymentApi;
private final AuditChecker auditChecker;
@Inject
- public PaymentChecker(final PaymentApi paymentApi, final AuditChecker auditChecker) {
+ public PaymentChecker(final PaymentApi paymentApi, final InvoicePaymentApi invoicePaymentApi, final AuditChecker auditChecker) {
this.paymentApi = paymentApi;
+ this.invoicePaymentApi = invoicePaymentApi;
this.auditChecker = auditChecker;
}
- public Payment checkPayment(final UUID accountId, final int paymentOrderingNumber, final CallContext context, ExpectedPaymentCheck expected) throws PaymentApiException {
+ public Payment checkPayment(final UUID accountId, final int paymentOrderingNumber, final CallContext context, final ExpectedPaymentCheck expected) throws PaymentApiException {
final List<Payment> payments = paymentApi.getAccountPayments(accountId, false, ImmutableList.<PluginProperty>of(), context);
Assert.assertEquals(payments.size(), paymentOrderingNumber);
final Payment payment = payments.get(paymentOrderingNumber - 1);
@@ -78,10 +82,19 @@ public class PaymentChecker {
private void checkPayment(final UUID accountId, final Payment payment, final CallContext context, final ExpectedPaymentCheck expected) {
Assert.assertEquals(payment.getAccountId(), accountId);
+ Assert.assertEquals(payment.getCurrency(), expected.getCurrency());
+
+ if (expected.getInvoiceId() != null) {
+ for (final InvoicePayment invoicePayment : invoicePaymentApi.getInvoicePayments(payment.getId(), context)) {
+ Assert.assertEquals(invoicePayment.getInvoiceId(), expected.getInvoiceId());
+ }
+ }
+
final PaymentTransaction transaction = getPurchaseTransaction(payment);
- Assert.assertTrue(transaction.getAmount().compareTo(expected.getAmount()) == 0);
+ Assert.assertTrue(transaction.getAmount().compareTo(expected.getAmount()) == 0, "Actual amount " + transaction.getAmount() + ", expected amount " + expected.getAmount());
Assert.assertEquals(transaction.getTransactionStatus(), expected.getStatus());
- Assert.assertEquals(payment.getCurrency(), expected.getCurrency());
+ Assert.assertEquals(transaction.getEffectiveDate().toLocalDate().compareTo(expected.getPaymentDate()), 0);
+
auditChecker.checkPaymentCreated(payment, context);
}
diff --git a/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v1.xml b/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v1.xml
index 11044e6..a8456f3 100644
--- a/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v1.xml
+++ b/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v1.xml
@@ -334,6 +334,8 @@
<plan>pistol-monthly</plan>
<plan>shotgun-monthly</plan>
<plan>shotgun-annual</plan>
+ <plan>laser-scope-monthly</plan>
+ <plan>extra-ammo-monthly</plan>
</plans>
</defaultPriceList>
<childPriceList name="SpecialDiscount">
diff --git a/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v2.xml b/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v2.xml
index e1bed72..d75891d 100644
--- a/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v2.xml
+++ b/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v2.xml
@@ -295,6 +295,8 @@
<plans>
<plan>shotgun-monthly</plan>
<plan>shotgun-annual</plan>
+ <plan>laser-scope-monthly</plan>
+ <plan>extra-ammo-monthly</plan>
</plans>
</defaultPriceList>
<childPriceList name="SpecialDiscount">
diff --git a/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v3.xml b/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v3.xml
index f5501e5..8ad8ba4 100644
--- a/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v3.xml
+++ b/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v3.xml
@@ -215,6 +215,9 @@
<defaultPriceList name="DEFAULT">
<plans>
<plan>shotgun-monthly</plan>
+ <plan>shotgun-annual</plan>
+ <plan>laser-scope-monthly</plan>
+ <plan>extra-ammo-monthly</plan>
</plans>
</defaultPriceList>
</priceLists>
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultCatalogUserApi.java b/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultCatalogUserApi.java
index 9d81dd1..f7898b5 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultCatalogUserApi.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultCatalogUserApi.java
@@ -18,7 +18,6 @@ package org.killbill.billing.catalog.api.user;
import javax.inject.Inject;
-import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.Catalog;
import org.killbill.billing.catalog.api.CatalogApiException;
@@ -53,7 +52,7 @@ public class DefaultCatalogUserApi implements CatalogUserApi {
@Override
public Catalog getCatalog(final String catalogName, final TenantContext tenantContext) throws CatalogApiException {
- final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(tenantContext);
+ final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(tenantContext);
return catalogService.getFullCatalog(internalTenantContext);
}
@@ -75,8 +74,8 @@ public class DefaultCatalogUserApi implements CatalogUserApi {
}
private InternalTenantContext createInternalTenantContext(final TenantContext tenantContext) {
- // Only tenantRecordId will be populated-- this is important to always create the (ehcache) key the same way
- return internalCallContextFactory.createInternalTenantContext(tenantContext);
+ // Only tenantRecordId will be populated -- this is important to always create the (ehcache) key the same way
+ return internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(tenantContext);
}
}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultDuration.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultDuration.java
index 4c3ed6c..6fc145a 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultDuration.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultDuration.java
@@ -21,6 +21,7 @@ import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
import org.joda.time.Period;
import org.killbill.billing.catalog.api.Duration;
@@ -73,9 +74,27 @@ public class DefaultDuration extends ValidatingConfig<StandaloneCatalog> impleme
case YEARS:
return dateTime.plusYears(number);
case UNLIMITED:
- return dateTime.plusYears(100);
default:
- return dateTime;
+ throw new IllegalStateException("Unexpected duration unit " + unit);
+ }
+ }
+
+ @Override
+ public LocalDate addToLocalDate(final LocalDate localDate) {
+ if ((number == null) && (unit != TimeUnit.UNLIMITED)) {
+ return localDate;
+ }
+
+ switch (unit) {
+ case DAYS:
+ return localDate.plusDays(number);
+ case MONTHS:
+ return localDate.plusMonths(number);
+ case YEARS:
+ return localDate.plusYears(number);
+ case UNLIMITED:
+ default:
+ throw new IllegalStateException("Unexpected duration unit " + unit);
}
}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
index 52e02e7..1955c5e 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -19,6 +21,8 @@ package org.killbill.billing.catalog;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
@@ -38,6 +42,7 @@ import org.killbill.billing.catalog.api.PhaseType;
import org.killbill.billing.catalog.api.Plan;
import org.killbill.billing.catalog.api.PlanPhase;
import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
+import org.killbill.billing.catalog.api.PriceList;
import org.killbill.billing.catalog.api.Product;
import org.killbill.billing.catalog.api.Recurring;
import org.killbill.xmlloader.ValidatingConfig;
@@ -73,6 +78,7 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements
@XmlElement(required = false)
private Integer plansAllowedInBundle = 1;
+ private PriceList priceList;
public DefaultPlan() {
initialPhases = new DefaultPlanPhase[0];
@@ -83,39 +89,34 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements
this.effectiveDateForExistingSubscriptons = in.getEffectiveDateForExistingSubscriptons();
this.product = (DefaultProduct) in.getProduct();
this.initialPhases = new DefaultPlanPhase[in.getInitialPhases().length];
- for (int i = 0; i< overrides.length - 1; i++) {
+ for (int i = 0; i < overrides.length - 1; i++) {
final DefaultPlanPhase newPhase = new DefaultPlanPhase(this, in.getInitialPhases()[i], overrides[i]);
initialPhases[i] = newPhase;
}
this.finalPhase = new DefaultPlanPhase(this, in.getFinalPhase(), overrides[overrides.length - 1]);
-
+ this.priceList = in.getPriceList();
}
- /* (non-Javadoc)
- * @see org.killbill.billing.catalog.IPlan#getEffectiveDateForExistingSubscriptons()
- */
+
@Override
public Date getEffectiveDateForExistingSubscriptons() {
return effectiveDateForExistingSubscriptons;
- } /* (non-Javadoc)
- * @see org.killbill.billing.catalog.IPlan#getPhases()
- */
+ }
@Override
public DefaultPlanPhase[] getInitialPhases() {
return initialPhases;
}
- /* (non-Javadoc)
- * @see org.killbill.billing.catalog.IPlan#getProduct()
- */
@Override
public Product getProduct() {
return product;
}
- /* (non-Javadoc)
- * @see org.killbill.billing.catalog.IPlan#getName()
- */
+ @Override
+ public PriceList getPriceList() {
+ return priceList;
+ }
+
@Override
public String getName() {
return name;
@@ -156,9 +157,6 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements
return finalPhase.getRecurring() != null ? finalPhase.getRecurring().getBillingPeriod() : BillingPeriod.NO_BILLING_PERIOD;
}
- /* (non-Javadoc)
- * @see org.killbill.billing.catalog.IPlan#getPlansAllowedInBundle()
- */
@Override
public int getPlansAllowedInBundle() {
return plansAllowedInBundle;
@@ -169,10 +167,8 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements
*/
@Override
public Iterator<PlanPhase> getInitialPhaseIterator() {
- final ArrayList<PlanPhase> list = new ArrayList<PlanPhase>();
- for (final DefaultPlanPhase p : initialPhases) {
- list.add(p);
- }
+ final Collection<PlanPhase> list = new ArrayList<PlanPhase>();
+ Collections.addAll(list, initialPhases);
return list.iterator();
}
@@ -189,12 +185,13 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements
p.initialize(catalog, sourceURI);
}
}
+ this.priceList = findPriceListForPlan(catalog);
}
@Override
public ValidationErrors validate(final StandaloneCatalog catalog, final ValidationErrors errors) {
if (effectiveDateForExistingSubscriptons != null &&
- catalog.getEffectiveDate().getTime() > effectiveDateForExistingSubscriptons.getTime()) {
+ catalog.getEffectiveDate().getTime() > effectiveDateForExistingSubscriptons.getTime()) {
errors.add(new ValidationError(String.format("Price effective date %s is before catalog effective date '%s'",
effectiveDateForExistingSubscriptons,
catalog.getEffectiveDate().getTime()),
@@ -226,6 +223,11 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements
return this;
}
+ public DefaultPlan setPriceList(final DefaultPriceList priceList) {
+ this.priceList = priceList;
+ return this;
+ }
+
public DefaultPlan setInitialPhases(final DefaultPlanPhase[] phases) {
this.initialPhases = phases;
return this;
@@ -238,8 +240,8 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements
@Override
public DateTime dateOfFirstRecurringNonZeroCharge(final DateTime subscriptionStartDate, final PhaseType initialPhaseType) {
- DateTime result = subscriptionStartDate.toDateTime();
- boolean skipPhase = initialPhaseType == null ? false : true;
+ DateTime result = subscriptionStartDate;
+ boolean skipPhase = initialPhaseType != null;
for (final PlanPhase phase : getAllPhases()) {
if (skipPhase) {
if (phase.getPhaseType() != initialPhaseType) {
@@ -303,8 +305,19 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements
@Override
public String toString() {
return "DefaultPlan [name=" + name + ", effectiveDateForExistingSubscriptons="
- + effectiveDateForExistingSubscriptons + ", product=" + product + ", initialPhases="
- + Arrays.toString(initialPhases) + ", finalPhase=" + finalPhase + ", plansAllowedInBundle="
- + plansAllowedInBundle + "]";
+ + effectiveDateForExistingSubscriptons + ", product=" + product + ", initialPhases="
+ + Arrays.toString(initialPhases) + ", finalPhase=" + finalPhase + ", plansAllowedInBundle="
+ + plansAllowedInBundle + "]";
+ }
+
+ private DefaultPriceList findPriceListForPlan(final StandaloneCatalog catalog) {
+ for (PriceList cur : catalog.getPriceLists().getAllPriceLists()) {
+ for (Plan p : cur.getPlans()) {
+ if (p.getName().equals(name)) {
+ return (DefaultPriceList) cur;
+ }
+ }
+ }
+ throw new IllegalStateException("Cannot extract pricelist for plan " + name);
}
}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java b/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
index 3b5f7de..7bf1326 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
@@ -55,13 +55,10 @@ public class VersionedCatalogLoader implements CatalogLoader {
this.internalCallContextFactory = internalCallContextFactory;
}
- /* (non-Javadoc)
- * @see org.killbill.billing.catalog.io.ICatalogLoader#loadDefaultCatalog(java.lang.String)
- */
@Override
public VersionedCatalog loadDefaultCatalog(final String uriString) throws CatalogApiException {
try {
- List<URI> xmlURIs;
+ final List<URI> xmlURIs;
if (uriString.endsWith(XML_EXTENSION)) { // Assume its an xml file
xmlURIs = new ArrayList<URI>();
xmlURIs.add(new URI(uriString));
@@ -77,7 +74,7 @@ public class VersionedCatalogLoader implements CatalogLoader {
}
return result;
} catch (Exception e) {
- throw new CatalogApiException(ErrorCode.CAT_INVALID_DEFAULT, "Problem encountered loading catalog ", e);
+ throw new CatalogApiException(ErrorCode.CAT_INVALID_DEFAULT, "Problem encountered loading catalog: ", e.getMessage());
}
}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/plugin/VersionedCatalogMapper.java b/catalog/src/main/java/org/killbill/billing/catalog/plugin/VersionedCatalogMapper.java
index 39ec3fa..1c6ba8c 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/plugin/VersionedCatalogMapper.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/plugin/VersionedCatalogMapper.java
@@ -17,10 +17,8 @@
package org.killbill.billing.catalog.plugin;
-import java.util.ArrayList;
import java.util.List;
-import javax.annotation.Nullable;
import javax.inject.Inject;
import org.killbill.billing.callcontext.InternalTenantContext;
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
index 0d02237..9520d62 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
@@ -281,9 +281,9 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
for (final DefaultPlan p : plans) {
p.initialize(catalog, sourceURI);
}
-
}
+
//////////////////////////////////////////////////////////////////////////////
//
// UNIT LIMIT
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
index ac2326a..1e14391 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -24,7 +24,6 @@ import java.util.regex.Matcher;
import javax.annotation.Nullable;
-import org.joda.time.DateTime;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.BillingActionPolicy;
@@ -140,16 +139,15 @@ public class StandaloneCatalogWithPriceOverride extends ValidatingConfig<Standal
return defaultPlan;
}
- final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(overrides.getCallContext());
- return priceOverride.getOrCreateOverriddenPlan(defaultPlan, new DateTime(getEffectiveDate()), overrides.getOverrides(), internalCallContext);
+ final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(overrides.getCallContext());
+ return priceOverride.getOrCreateOverriddenPlan(defaultPlan, CatalogDateHelper.toUTCDateTime(getEffectiveDate()), overrides.getOverrides(), internalCallContext);
}
@Override
public Plan findCurrentPlan(final String planName) throws CatalogApiException {
-
final Matcher m = DefaultPriceOverride.CUSTOM_PLAN_NAME_PATTERN.matcher(planName);
if (m.matches()) {
- final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(tenantRecordId, null);
+ final InternalTenantContext internalTenantContext = createInternalTenantContext();
return priceOverride.getOverriddenPlan(planName, standaloneCatalog, internalTenantContext);
}
return standaloneCatalog.findCurrentPlan(planName);
@@ -165,8 +163,8 @@ public class StandaloneCatalogWithPriceOverride extends ValidatingConfig<Standal
final String planName = DefaultPlanPhase.planName(phaseName);
final Matcher m = DefaultPriceOverride.CUSTOM_PLAN_NAME_PATTERN.matcher(planName);
if (m.matches()) {
- final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(tenantRecordId, null);
- Plan plan = priceOverride.getOverriddenPlan(planName, standaloneCatalog, internalTenantContext);
+ final InternalTenantContext internalTenantContext = createInternalTenantContext();
+ final Plan plan = priceOverride.getOverriddenPlan(planName, standaloneCatalog, internalTenantContext);
return plan.findPhase(phaseName);
}
return standaloneCatalog.findCurrentPhase(phaseName);
@@ -241,4 +239,7 @@ public class StandaloneCatalogWithPriceOverride extends ValidatingConfig<Standal
return standaloneCatalog.findCurrentPriceList(priceListName);
}
+ private InternalTenantContext createInternalTenantContext() {
+ return internalCallContextFactory.createInternalTenantContext(tenantRecordId, null);
+ }
}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
index b0aa352..b4a222a 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -65,21 +65,20 @@ import org.killbill.xmlloader.ValidationErrors;
public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPriceOverride> implements Catalog, StaticCatalog {
private final Clock clock;
+ @XmlElement(name = "catalogVersion", required = true)
+ private final List<StandaloneCatalogWithPriceOverride> versions;
private String catalogName;
private BillingMode recurringBillingMode;
- @XmlElement(name = "catalogVersion", required = true)
- private List<StandaloneCatalogWithPriceOverride> versions;
-
// Required for JAXB deserialization
public VersionedCatalog() {
this.clock = null;
- versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
+ this.versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
}
public VersionedCatalog(final Clock clock) {
this.clock = clock;
- versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
+ this.versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
}
public VersionedCatalog(final Clock clock, final String catalogName, final BillingMode recurringBillingMode, final List<StandaloneCatalogWithPriceOverride> versions, final InternalTenantContext tenantContext) {
@@ -93,7 +92,6 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
}
}
-
//
// Private methods
//
@@ -154,16 +152,16 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
final DateTime subscriptionStartDate)
throws CatalogApiException {
final List<StandaloneCatalogWithPriceOverride> catalogs = versionsBeforeDate(requestedDate.toDate());
- if (catalogs.size() == 0) {
+ if (catalogs.isEmpty()) {
throw new CatalogApiException(ErrorCode.CAT_NO_CATALOG_FOR_GIVEN_DATE, requestedDate.toDate().toString());
}
for (int i = catalogs.size() - 1; i >= 0; i--) { // Working backwards to find the latest applicable plan
final StandaloneCatalogWithPriceOverride c = catalogs.get(i);
- Plan plan;
+ final Plan plan;
try {
plan = wrapper.findPlan(c);
- } catch (CatalogApiException e) {
+ } catch (final CatalogApiException e) {
if (e.getCode() != ErrorCode.CAT_NO_SUCH_PLAN.getCode()) {
throw e;
} else {
@@ -172,12 +170,12 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
}
}
- DateTime catalogEffectiveDate = new DateTime(c.getEffectiveDate());
+ final DateTime catalogEffectiveDate = CatalogDateHelper.toUTCDateTime(c.getEffectiveDate());
if (!subscriptionStartDate.isBefore(catalogEffectiveDate)) { // Its a new subscription this plan always applies
return plan;
} else { //Its an existing subscription
if (plan.getEffectiveDateForExistingSubscriptons() != null) { //if it is null any change to this does not apply to existing subscriptions
- final DateTime existingSubscriptionDate = new DateTime(plan.getEffectiveDateForExistingSubscriptons());
+ final DateTime existingSubscriptionDate = CatalogDateHelper.toUTCDateTime(plan.getEffectiveDateForExistingSubscriptons());
if (requestedDate.isAfter(existingSubscriptionDate)) { // this plan is now applicable to existing subs
return plan;
}
@@ -271,10 +269,10 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
@Override
public Plan createOrFindPlan(final String productName,
- final BillingPeriod term,
- final String priceListName,
- final PlanPhasePriceOverridesWithCallContext overrides,
- final DateTime requestedDate)
+ final BillingPeriod term,
+ final String priceListName,
+ final PlanPhasePriceOverridesWithCallContext overrides,
+ final DateTime requestedDate)
throws CatalogApiException {
return versionForDate(requestedDate).createOrFindCurrentPlan(productName, term, priceListName, overrides);
}
@@ -289,11 +287,11 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
@Override
public Plan createOrFindPlan(final String productName,
- final BillingPeriod term,
- final String priceListName,
- final PlanPhasePriceOverridesWithCallContext overrides,
- final DateTime requestedDate,
- final DateTime subscriptionStartDate)
+ final BillingPeriod term,
+ final String priceListName,
+ final PlanPhasePriceOverridesWithCallContext overrides,
+ final DateTime requestedDate,
+ final DateTime subscriptionStartDate)
throws CatalogApiException {
return findPlan(new PlanRequestWrapper(productName, term, priceListName, overrides), requestedDate, subscriptionStartDate);
}
@@ -434,7 +432,7 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
@Override
public Plan createOrFindCurrentPlan(final String productName, final BillingPeriod term,
- final String priceList, PlanPhasePriceOverridesWithCallContext overrides) throws CatalogApiException {
+ final String priceList, final PlanPhasePriceOverridesWithCallContext overrides) throws CatalogApiException {
return versionForDate(clock.getUTCNow()).createOrFindCurrentPlan(productName, term, priceList, overrides);
}
@@ -512,7 +510,7 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
}
@Override
- public boolean compliesWithLimits(String phaseName, String unit, double value) throws CatalogApiException {
+ public boolean compliesWithLimits(final String phaseName, final String unit, final double value) throws CatalogApiException {
return versionForDate(clock.getUTCNow()).compliesWithLimits(phaseName, unit, value);
}
}
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/glue/TestCatalogModule.java b/catalog/src/test/java/org/killbill/billing/catalog/glue/TestCatalogModule.java
index b6f5354..e3bef69 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/glue/TestCatalogModule.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/glue/TestCatalogModule.java
@@ -19,6 +19,7 @@
package org.killbill.billing.catalog.glue;
import org.killbill.billing.GuicyKillbillTestNoDBModule;
+import org.killbill.billing.mock.glue.MockAccountModule;
import org.killbill.billing.mock.glue.MockNonEntityDaoModule;
import org.killbill.billing.mock.glue.MockTenantModule;
import org.killbill.billing.platform.api.KillbillConfigSource;
@@ -36,5 +37,6 @@ public class TestCatalogModule extends CatalogModule {
install(new MockNonEntityDaoModule(configSource));
install(new CacheModule(configSource));
install(new MockTenantModule(configSource));
+ install(new MockAccountModule(configSource));
}
}
diff --git a/catalog/src/test/resources/catalogTest.xml b/catalog/src/test/resources/catalogTest.xml
index 56576c4..4b1ee25 100644
--- a/catalog/src/test/resources/catalogTest.xml
+++ b/catalog/src/test/resources/catalogTest.xml
@@ -50,6 +50,7 @@
<available>
<addonProduct>Cleaning</addonProduct>
<addonProduct>Bullets</addonProduct>
+ <addonProduct>Refurbish-Maintenance</addonProduct>
</available>
</product>
<product name="Shotgun">
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml
index 978b238..b2b9276 100644
--- a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml
@@ -276,6 +276,8 @@
<plan>pistol-monthly</plan>
<plan>shotgun-monthly</plan>
<plan>shotgun-annual</plan>
+ <plan>laser-scope-monthly</plan>
+ <plan>extra-ammo-monthly</plan>
</plans>
</defaultPriceList>
</priceLists>
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml
index 9801e42..251678b 100644
--- a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml
@@ -278,6 +278,8 @@
<plan>pistol-monthly</plan>
<plan>shotgun-monthly</plan>
<plan>shotgun-annual</plan>
+ <plan>laser-scope-monthly</plan>
+ <plan>extra-ammo-monthly</plan>
</plans>
</defaultPriceList>
</priceLists>
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml
index 453a143..bdeca61 100644
--- a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml
@@ -277,6 +277,8 @@
<plan>pistol-monthly</plan>
<plan>shotgun-monthly</plan>
<plan>shotgun-annual</plan>
+ <plan>laser-scope-monthly</plan>
+ <plan>extra-ammo-monthly</plan>
</plans>
</defaultPriceList>
</priceLists>
diff --git a/catalog/src/test/resources/WeaponsHireSmall.xml b/catalog/src/test/resources/WeaponsHireSmall.xml
index c0501d3..5adfb99 100644
--- a/catalog/src/test/resources/WeaponsHireSmall.xml
+++ b/catalog/src/test/resources/WeaponsHireSmall.xml
@@ -310,6 +310,8 @@
<plan>pistol-monthly</plan>
<plan>shotgun-monthly</plan>
<plan>shotgun-annual</plan>
+ <plan>laser-scope-monthly</plan>
+ <plan>extra-ammo-monthly</plan>
</plans>
</defaultPriceList>
</priceLists>
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java
index 065076f..429eafe 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -24,13 +24,15 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.ListIterator;
import java.util.Map;
+import java.util.Set;
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.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Plan;
import org.killbill.billing.catalog.api.PlanPhase;
@@ -43,8 +45,11 @@ import org.killbill.billing.junction.DefaultBlockingState;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
// Given an event stream (across one or multiple entitlements), insert the blocking events at the right place
public class BlockingStateOrdering extends EntitlementOrderingBase {
@@ -53,11 +58,14 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
private BlockingStateOrdering() {}
- public static void insertSorted(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone, final LinkedList<SubscriptionEvent> result) {
- INSTANCE.computeEvents(entitlements, accountTimeZone, result);
+ public static void insertSorted(final Iterable<Entitlement> entitlements, final InternalTenantContext internalTenantContext, final LinkedList<SubscriptionEvent> inputAndOutputResult) {
+ INSTANCE.computeEvents(entitlements, internalTenantContext, inputAndOutputResult);
+
}
- private void computeEvents(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone, final LinkedList<SubscriptionEvent> result) {
+ private void computeEvents(final Iterable<Entitlement> entitlements, final InternalTenantContext internalTenantContext, final LinkedList<SubscriptionEvent> inputAndOutputResult) {
+
+
final Collection<UUID> allEntitlementUUIDs = new HashSet<UUID>();
final Collection<BlockingState> blockingStates = new LinkedList<BlockingState>();
for (final Entitlement entitlement : entitlements) {
@@ -66,16 +74,19 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
blockingStates.addAll(((DefaultEntitlement) entitlement).getEventsStream().getBlockingStates());
}
+ final SupportForOlderVersionThan_0_17_X backwardCompatibleContext = new SupportForOlderVersionThan_0_17_X(inputAndOutputResult, blockingStates);
+
// Trust the incoming ordering here: blocking states were sorted using ProxyBlockingStateDao#sortedCopy
- for (final BlockingState bs : blockingStates) {
- final List<SubscriptionEvent> newEvents = new ArrayList<SubscriptionEvent>();
- final int index = insertFromBlockingEvent(accountTimeZone, allEntitlementUUIDs, result, bs, bs.getEffectiveDate(), newEvents);
- insertAfterIndex(result, newEvents, index);
+ for (final BlockingState currentBlockingState : blockingStates) {
+ final List<SubscriptionEvent> outputNewEvents = new ArrayList<SubscriptionEvent>();
+ final int index = insertFromBlockingEvent(allEntitlementUUIDs, currentBlockingState, inputAndOutputResult, backwardCompatibleContext, internalTenantContext, outputNewEvents);
+ insertAfterIndex(inputAndOutputResult, outputNewEvents, index);
}
+ backwardCompatibleContext.addMissing_START_ENTITLEMENT(inputAndOutputResult, internalTenantContext);
}
// Returns the index and the newEvents generated from the incoming blocking state event. Those new events will all be created for the same effectiveDate and should be ordered.
- private int insertFromBlockingEvent(final DateTimeZone accountTimeZone, final Collection<UUID> allEntitlementUUIDs, final List<SubscriptionEvent> result, final BlockingState bs, final DateTime bsEffectiveDate, final List<SubscriptionEvent> newEvents) {
+ private int insertFromBlockingEvent(final Collection<UUID> allEntitlementUUIDs, final BlockingState currentBlockingState, final List<SubscriptionEvent> inputExistingEvents, final SupportForOlderVersionThan_0_17_X backwardCompatibleContext, final InternalTenantContext internalTenantContext, final Collection<SubscriptionEvent> outputNewEvents) {
// Keep the current state per entitlement
final Map<UUID, TargetState> targetStates = new HashMap<UUID, TargetState>();
for (final UUID cur : allEntitlementUUIDs) {
@@ -86,12 +97,12 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
// Find out where to insert next event, and calculate current state for each entitlement at the position where we stop.
//
int index = -1;
- final Iterator<SubscriptionEvent> it = result.iterator();
+ final Iterator<SubscriptionEvent> it = inputExistingEvents.iterator();
// Where we need to insert in that stream
DefaultSubscriptionEvent curInsertion = null;
while (it.hasNext()) {
final DefaultSubscriptionEvent cur = (DefaultSubscriptionEvent) it.next();
- final int compEffectiveDate = bsEffectiveDate.compareTo(cur.getEffectiveDateTime());
+ final int compEffectiveDate = currentBlockingState.getEffectiveDate().compareTo(cur.getEffectiveDateTime());
final boolean shouldContinue = (compEffectiveDate >= 0);
if (!shouldContinue) {
break;
@@ -107,6 +118,10 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
curTargetState.setEntitlementStopped();
break;
case START_BILLING:
+ // For older subscriptions we miss the START_ENTITLEMENT (the START_BILLING marks both start of billing and entitlement)
+ if (backwardCompatibleContext.isOlderEntitlement(cur.getEntitlementId())) {
+ curTargetState.setEntitlementStarted();
+ }
curTargetState.setBillingStarted();
break;
case PAUSE_BILLING:
@@ -124,17 +139,17 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
}
// Extract the list of targets based on the type of blocking state
- final List<UUID> targetEntitlementIds = bs.getType() == BlockingStateType.SUBSCRIPTION ? ImmutableList.<UUID>of(bs.getBlockedId()) :
+ final List<UUID> targetEntitlementIds = currentBlockingState.getType() == BlockingStateType.SUBSCRIPTION ? ImmutableList.<UUID>of(currentBlockingState.getBlockedId()) :
ImmutableList.<UUID>copyOf(allEntitlementUUIDs);
// For each target compute the new events that should be inserted in the stream
for (final UUID targetEntitlementId : targetEntitlementIds) {
- final SubscriptionEvent[] prevNext = findPrevNext(result, targetEntitlementId, curInsertion);
+ final SubscriptionEvent[] prevNext = findPrevNext(inputExistingEvents, targetEntitlementId, curInsertion);
final TargetState curTargetState = targetStates.get(targetEntitlementId);
- final List<SubscriptionEventType> eventTypes = curTargetState.addStateAndReturnEventTypes(bs);
+ final List<SubscriptionEventType> eventTypes = curTargetState.addStateAndReturnEventTypes(currentBlockingState);
for (final SubscriptionEventType t : eventTypes) {
- newEvents.add(toSubscriptionEvent(prevNext[0], prevNext[1], targetEntitlementId, bs, t, accountTimeZone));
+ outputNewEvents.add(toSubscriptionEvent(prevNext[0], prevNext[1], targetEntitlementId, currentBlockingState, t, internalTenantContext));
}
}
return index;
@@ -176,7 +191,8 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
}
private SubscriptionEvent toSubscriptionEvent(@Nullable final SubscriptionEvent prev, @Nullable final SubscriptionEvent next,
- final UUID entitlementId, final BlockingState in, final SubscriptionEventType eventType, final DateTimeZone accountTimeZone) {
+ final UUID entitlementId, final BlockingState in, final SubscriptionEventType eventType,
+ final InternalTenantContext internalTenantContext) {
final Product prevProduct;
final Plan prevPlan;
final PlanPhase prevPlanPhase;
@@ -203,8 +219,10 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
final PlanPhase nextPlanPhase;
final PriceList nextPriceList;
final BillingPeriod nextBillingPeriod;
- if (SubscriptionEventType.PAUSE_ENTITLEMENT.equals(eventType) || SubscriptionEventType.PAUSE_BILLING.equals(eventType) ||
- SubscriptionEventType.RESUME_ENTITLEMENT.equals(eventType) || SubscriptionEventType.RESUME_BILLING.equals(eventType) ||
+ if (SubscriptionEventType.PAUSE_ENTITLEMENT.equals(eventType) ||
+ SubscriptionEventType.PAUSE_BILLING.equals(eventType) ||
+ SubscriptionEventType.RESUME_ENTITLEMENT.equals(eventType) ||
+ SubscriptionEventType.RESUME_BILLING.equals(eventType) ||
(SubscriptionEventType.SERVICE_STATE_CHANGE.equals(eventType) && (prev == null || (!SubscriptionEventType.STOP_ENTITLEMENT.equals(prev.getSubscriptionEventType()) && !SubscriptionEventType.STOP_BILLING.equals(prev.getSubscriptionEventType()))))) {
// Enforce next = prev for pause/resume events as well as service changes
nextProduct = prevProduct;
@@ -257,7 +275,7 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
nextPriceList,
nextBillingPeriod,
in.getCreatedDate(),
- accountTimeZone);
+ internalTenantContext);
}
private void insertAfterIndex(final LinkedList<SubscriptionEvent> original, final Collection<SubscriptionEvent> newEvents, final int index) {
@@ -343,17 +361,22 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
bs.getStateName(),
bs.getService(),
bs.isBlockChange(),
- (bs.isBlockEntitlement() && isEntitlementStarted && !isEntitlementStopped),
+ (bs.isBlockEntitlement() && isEntitlementStarted && !isEntitlementStopped),
(bs.isBlockBilling() && isBillingStarted && !isBillingStopped),
bs.getEffectiveDate());
final List<SubscriptionEventType> result = new ArrayList<SubscriptionEventType>(4);
- if (fixedBlockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CANCELLED)) {
+ if (fixedBlockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_START)) {
+ isEntitlementStarted = true;
+ result.add(SubscriptionEventType.START_ENTITLEMENT);
+ return result;
+ } else if (fixedBlockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CANCELLED)) {
isEntitlementStopped = true;
result.add(SubscriptionEventType.STOP_ENTITLEMENT);
return result;
}
+
//
// We look at the effect of the incoming event for the specific service, and then recompute the state after so we can compare if anything has changed
// across all services
@@ -370,7 +393,7 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
}
final BlockingAggregator stateAfter = getState();
- final boolean shouldResumeEntitlement = isEntitlementStarted && !isEntitlementStopped && stateBefore.isBlockEntitlement() && !stateAfter.isBlockEntitlement();
+ final boolean shouldResumeEntitlement = isEntitlementStarted && !isEntitlementStopped && stateBefore.isBlockEntitlement() && !stateAfter.isBlockEntitlement();
if (shouldResumeEntitlement) {
result.add(SubscriptionEventType.RESUME_ENTITLEMENT);
}
@@ -379,7 +402,7 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
result.add(SubscriptionEventType.RESUME_BILLING);
}
- final boolean shouldBlockEntitlement = isEntitlementStarted && !isEntitlementStopped && !stateBefore.isBlockEntitlement() && stateAfter.isBlockEntitlement();
+ final boolean shouldBlockEntitlement = isEntitlementStarted && !isEntitlementStopped && !stateBefore.isBlockEntitlement() && stateAfter.isBlockEntitlement();
if (shouldBlockEntitlement) {
result.add(SubscriptionEventType.PAUSE_ENTITLEMENT);
}
@@ -402,4 +425,85 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
return aggrBefore;
}
}
+
+ //
+ // The logic to add the missing START_ENTITLEMENT for older subscriptions is contained in this class. When we want/need to drop backward compatibility we can
+ // simply drop this class and where it is called.
+ //
+ private static class SupportForOlderVersionThan_0_17_X {
+
+ private final Set<UUID> olderEntitlementSet;
+
+ public SupportForOlderVersionThan_0_17_X(final List<SubscriptionEvent> initialEntitlementEvents, final Collection<BlockingState> blockingStates) {
+ this.olderEntitlementSet = computeOlderEntitlementSet(initialEntitlementEvents, blockingStates);
+ }
+
+ public boolean isOlderEntitlement(final UUID entitlementId) {
+ return olderEntitlementSet.contains(entitlementId);
+ }
+
+ public void addMissing_START_ENTITLEMENT(final LinkedList<SubscriptionEvent> inputAndOutputResult, final InternalTenantContext internalTenantContext) {
+
+ // Insert missing START_ENTITLEMENT right before START_BILLING (same event as START_BILLING but with different type=START_ENTITLEMENT to be compatible with old code)
+ final ListIterator<SubscriptionEvent> it = inputAndOutputResult.listIterator();
+ while (it.hasNext()) {
+ final SubscriptionEvent cur = it.next();
+ if (cur.getSubscriptionEventType() == SubscriptionEventType.START_BILLING && olderEntitlementSet.contains(cur.getEntitlementId())) {
+ final SubscriptionEvent newEntitlementStartEvent = new DefaultSubscriptionEvent(cur.getId(),
+ cur.getEntitlementId(),
+ internalTenantContext.toUTCDateTime(cur.getEffectiveDate()),
+ SubscriptionEventType.START_ENTITLEMENT,
+ false,
+ false,
+ DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ SubscriptionEventType.START_ENTITLEMENT.toString(),
+ cur.getPrevProduct(),
+ cur.getPrevPlan(),
+ cur.getPrevPhase(),
+ cur.getPrevPriceList(),
+ cur.getPrevBillingPeriod(),
+ cur.getNextProduct(),
+ cur.getNextPlan(),
+ cur.getNextPhase(),
+ cur.getNextPriceList(),
+ cur.getNextBillingPeriod(),
+ internalTenantContext.toUTCDateTime(cur.getEffectiveDate()),
+ internalTenantContext);
+ it.previous();
+ it.add(newEntitlementStartEvent);
+ it.next();
+ }
+ }
+ }
+
+ private Set<UUID> computeOlderEntitlementSet(final List<SubscriptionEvent> initialEntitlementEvents, final Collection<BlockingState> blockingStates) {
+
+ final Set<UUID> START_BILLING_entitlementIdSet = ImmutableSet.copyOf(Iterables.transform(Iterables.filter(initialEntitlementEvents, new Predicate<SubscriptionEvent>() {
+ @Override
+ public boolean apply(final SubscriptionEvent input) {
+ return input.getSubscriptionEventType() == SubscriptionEventType.START_BILLING;
+ }
+ }), new Function<SubscriptionEvent, UUID>() {
+ @Override
+ public UUID apply(final SubscriptionEvent input) {
+ return input.getEntitlementId();
+ }
+ }));
+
+ final Set<UUID> ENT_STATE_START_entitlementIdSet = ImmutableSet.copyOf(Iterables.transform(Iterables.filter(blockingStates, new Predicate<BlockingState>() {
+ @Override
+ public boolean apply(final BlockingState input) {
+ return input.getService().equals(DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME) && input.getStateName().equals(DefaultEntitlementApi.ENT_STATE_START);
+ }
+ }), new Function<BlockingState, UUID>() {
+ @Override
+ public UUID apply(final BlockingState input) {
+ return input.getBlockedId();
+ }
+ }));
+
+ return Sets.<UUID>difference(START_BILLING_entitlementIdSet, ENT_STATE_START_entitlementIdSet);
+ }
+ }
+
}
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultBlockingTransitionInternalEvent.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultBlockingTransitionInternalEvent.java
index f32a19c..67b0ae2 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultBlockingTransitionInternalEvent.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultBlockingTransitionInternalEvent.java
@@ -18,6 +18,7 @@ package org.killbill.billing.entitlement.api;
import java.util.UUID;
+import org.joda.time.DateTime;
import org.killbill.billing.events.BlockingTransitionInternalEvent;
import org.killbill.billing.events.BusEventBase;
@@ -28,6 +29,9 @@ import com.fasterxml.jackson.annotation.JsonProperty;
public class DefaultBlockingTransitionInternalEvent extends BusEventBase implements BlockingTransitionInternalEvent {
private final UUID blockableId;
+ private final String stateName;
+ private final String service;
+ private final DateTime effectiveDate;
private final BlockingStateType blockingType;
private final Boolean isTransitionedToBlockedBilling;
private final Boolean isTransitionedToUnblockedBilling;
@@ -36,6 +40,9 @@ public class DefaultBlockingTransitionInternalEvent extends BusEventBase impleme
@JsonCreator
public DefaultBlockingTransitionInternalEvent(@JsonProperty("blockableId") final UUID blockableId,
+ @JsonProperty("stateName") final String stateName,
+ @JsonProperty("service") final String service,
+ @JsonProperty("effectiveDate") final DateTime effectiveDate,
@JsonProperty("blockingType") final BlockingStateType blockingType,
@JsonProperty("isTransitionedToBlockedBilling") final Boolean transitionedToBlockedBilling,
@JsonProperty("isTransitionedToUnblockedBilling") final Boolean transitionedToUnblockedBilling,
@@ -47,6 +54,9 @@ public class DefaultBlockingTransitionInternalEvent extends BusEventBase impleme
super(searchKey1, searchKey2, userToken);
this.blockableId = blockableId;
this.blockingType = blockingType;
+ this.service = service;
+ this.stateName = stateName;
+ this.effectiveDate = effectiveDate;
isTransitionedToBlockedBilling = transitionedToBlockedBilling;
isTransitionedToUnblockedBilling = transitionedToUnblockedBilling;
isTransitionedToBlockedEntitlement = transitionedToBlockedEntitlement;
@@ -63,6 +73,21 @@ public class DefaultBlockingTransitionInternalEvent extends BusEventBase impleme
return blockingType;
}
+ @Override
+ public String getStateName() {
+ return stateName;
+ }
+
+ @Override
+ public String getService() {
+ return service;
+ }
+
+ @Override
+ public DateTime getEffectiveDate() {
+ return effectiveDate;
+ }
+
@JsonProperty("isTransitionedToBlockedBilling")
@Override
public Boolean isTransitionedToBlockedBilling() {
@@ -110,6 +135,15 @@ public class DefaultBlockingTransitionInternalEvent extends BusEventBase impleme
if (blockingType != that.blockingType) {
return false;
}
+ if (stateName != null ? !stateName.equals(that.stateName) : that.stateName != null) {
+ return false;
+ }
+ if (service != null ? !service.equals(that.service) : that.service != null) {
+ return false;
+ }
+ if (effectiveDate != null ? effectiveDate.compareTo(that.effectiveDate) != 0 : that.effectiveDate != null) {
+ return false;
+ }
if (isTransitionedToBlockedBilling != null ? !isTransitionedToBlockedBilling.equals(that.isTransitionedToBlockedBilling) : that.isTransitionedToBlockedBilling != null) {
return false;
}
@@ -130,6 +164,9 @@ public class DefaultBlockingTransitionInternalEvent extends BusEventBase impleme
public int hashCode() {
int result = blockableId != null ? blockableId.hashCode() : 0;
result = 31 * result + (blockingType != null ? blockingType.hashCode() : 0);
+ result = 31 * result + (stateName != null ? stateName.hashCode() : 0);
+ result = 31 * result + (service != null ? service.hashCode() : 0);
+ result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
result = 31 * result + (isTransitionedToBlockedBilling != null ? isTransitionedToBlockedBilling.hashCode() : 0);
result = 31 * result + (isTransitionedToUnblockedBilling != null ? isTransitionedToUnblockedBilling.hashCode() : 0);
result = 31 * result + (isTransitionedToBlockedEntitlement != null ? isTransitionedToBlockedEntitlement.hashCode() : 0);
@@ -142,6 +179,9 @@ public class DefaultBlockingTransitionInternalEvent extends BusEventBase impleme
final StringBuilder sb = new StringBuilder("DefaultBlockingTransitionInternalEvent{");
sb.append("blockableId=").append(blockableId);
sb.append(", blockingType=").append(blockingType);
+ sb.append(", stateName=").append(stateName);
+ sb.append(", service=").append(service);
+ sb.append(", effectiveDate=").append(effectiveDate);
sb.append(", isTransitionedToBlockedBilling=").append(isTransitionedToBlockedBilling);
sb.append(", isTransitionedToUnblockedBilling=").append(isTransitionedToUnblockedBilling);
sb.append(", isTransitionedToBlockedEntitlement=").append(isTransitionedToBlockedEntitlement);
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
index 93462b9..4cf7938 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
@@ -25,11 +25,14 @@ import java.util.LinkedList;
import java.util.List;
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.ErrorCode;
import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.BillingActionPolicy;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Plan;
@@ -76,6 +79,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
private final SecurityApi securityApi;
protected final EventsStreamBuilder eventsStreamBuilder;
protected final EntitlementDateHelper dateHelper;
+ protected final InternalTenantContext internalTenantContext;
protected final InternalCallContextFactory internalCallContextFactory;
protected final Clock clock;
protected final BlockingChecker checker;
@@ -90,7 +94,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
protected EventsStream eventsStream;
- public DefaultEntitlement(final UUID entitlementId, final EventsStreamBuilder eventsStreamBuilder,
+ public DefaultEntitlement(final UUID accountId, final UUID entitlementId, final EventsStreamBuilder eventsStreamBuilder,
final EntitlementApi entitlementApi, final EntitlementPluginExecution pluginExecution, final BlockingStateDao blockingStateDao,
final SubscriptionBaseInternalApi subscriptionInternalApi, final BlockingChecker checker,
final NotificationQueueService notificationQueueService, final EntitlementUtils entitlementUtils,
@@ -98,14 +102,14 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
final InternalCallContextFactory internalCallContextFactory, final TenantContext tenantContext) throws EntitlementApiException {
this(eventsStreamBuilder.buildForEntitlement(entitlementId, tenantContext), eventsStreamBuilder,
entitlementApi, pluginExecution, blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
- entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory);
+ entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory.createInternalTenantContext(accountId, tenantContext), internalCallContextFactory);
}
public DefaultEntitlement(final EventsStream eventsStream, final EventsStreamBuilder eventsStreamBuilder,
final EntitlementApi entitlementApi, final EntitlementPluginExecution pluginExecution, final BlockingStateDao blockingStateDao,
final SubscriptionBaseInternalApi subscriptionInternalApi, final BlockingChecker checker,
final NotificationQueueService notificationQueueService, final EntitlementUtils entitlementUtils,
- final EntitlementDateHelper dateHelper, final Clock clock, final SecurityApi securityApi, final InternalCallContextFactory internalCallContextFactory) {
+ final EntitlementDateHelper dateHelper, final Clock clock, final SecurityApi securityApi, final InternalTenantContext internalTenantContext, final InternalCallContextFactory internalCallContextFactory) {
super(eventsStream.getEntitlementId(), eventsStream.getSubscriptionBase().getCreatedDate(), eventsStream.getSubscriptionBase().getUpdatedDate());
this.eventsStreamBuilder = eventsStreamBuilder;
this.eventsStream = eventsStream;
@@ -113,6 +117,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
this.entitlementApi = entitlementApi;
this.pluginExecution = pluginExecution;
this.subscriptionInternalApi = subscriptionInternalApi;
+ this.internalTenantContext = internalTenantContext;
this.internalCallContextFactory = internalCallContextFactory;
this.clock = clock;
this.securityApi = securityApi;
@@ -135,6 +140,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
in.getDateHelper(),
in.getClock(),
in.getSecurityApi(),
+ in.getInternalTenantContext(),
in.getInternalCallContextFactory());
}
@@ -164,6 +170,10 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
return dateHelper;
}
+ public InternalTenantContext getInternalTenantContext() {
+ return internalTenantContext;
+ }
+
public InternalCallContextFactory getInternalCallContextFactory() {
return internalCallContextFactory;
}
@@ -236,7 +246,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
@Override
public LocalDate getEffectiveStartDate() {
- return new LocalDate(getSubscriptionBase().getStartDate(), eventsStream.getAccountTimeZone());
+ return eventsStream.getEntitlementEffectiveStartDate();
}
@Override
@@ -281,20 +291,23 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
}
@Override
- public Entitlement cancelEntitlementWithDate(final LocalDate localCancelDate, final boolean overrideBillingEffectiveDate, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+ public Entitlement cancelEntitlementWithDate(@Nullable final LocalDate entitlementEffectiveDate, final boolean overrideBillingEffectiveDate, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
checkForPermissions(Permission.ENTITLEMENT_CAN_CANCEL, callContext);
// Get the latest state from disk
refresh(callContext);
+ final LocalDate billingEffectiveDate = overrideBillingEffectiveDate ? entitlementEffectiveDate : null;
final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.CANCEL_SUBSCRIPTION,
getAccountId(),
null,
getBundleId(),
getExternalKey(),
null,
- localCancelDate,
+ entitlementEffectiveDate,
+ billingEffectiveDate,
+ null,
properties,
callContext);
@@ -308,7 +321,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
}
final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
- final DateTime effectiveCancelDate = dateHelper.fromLocalDateAndReferenceTime(localCancelDate, getSubscriptionBase().getStartDate(), contextWithValidAccountRecordId);
+ final DateTime effectiveCancelDate = dateHelper.fromLocalDateAndReferenceTime(entitlementEffectiveDate, contextWithValidAccountRecordId);
try {
if (overrideBillingEffectiveDate) {
getSubscriptionBase().cancelWithDate(effectiveCancelDate, callContext);
@@ -345,38 +358,59 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
// Get the latest state from disk
refresh(callContext);
- if (eventsStream.isSubscriptionCancelled()) {
- throw new EntitlementApiException(ErrorCode.SUB_UNCANCEL_BAD_STATE, getId());
- }
+ final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.UNCANCEL_SUBSCRIPTION,
+ getAccountId(),
+ null,
+ getBundleId(),
+ getExternalKey(),
+ null,
+ null,
+ null,
+ null,
+ properties,
+ callContext);
- final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
- final Collection<BlockingState> pendingEntitlementCancellationEvents = eventsStream.getPendingEntitlementCancellationEvents();
- if (eventsStream.isEntitlementCancelled()) {
- final BlockingState cancellationEvent = eventsStream.getEntitlementCancellationEvent();
- blockingStateDao.unactiveBlockingState(cancellationEvent.getId(), contextWithValidAccountRecordId);
- } else if (pendingEntitlementCancellationEvents.size() > 0) {
- // Reactivate entitlements
- // See also https://github.com/killbill/killbill/issues/111
- //
- // Today we only support cancellation at SUBSCRIPTION level (Not ACCOUNT or BUNDLE), so we should really have only
- // one future event in the list
- //
- for (final BlockingState futureCancellation : pendingEntitlementCancellationEvents) {
- blockingStateDao.unactiveBlockingState(futureCancellation.getId(), contextWithValidAccountRecordId);
- }
- } else {
- // Entitlement is NOT cancelled (or future cancelled), there is nothing to do
- throw new EntitlementApiException(ErrorCode.SUB_UNCANCEL_BAD_STATE, getId());
- }
+ final WithEntitlementPlugin<Void> uncancelEntitlementWithPlugin = new WithEntitlementPlugin<Void>() {
- // If billing was previously cancelled, reactivate
- if (getSubscriptionBase().getFutureEndDate() != null) {
- try {
- getSubscriptionBase().uncancel(callContext);
- } catch (final SubscriptionBaseApiException e) {
- throw new EntitlementApiException(e);
+ @Override
+ public Void doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
+ if (eventsStream.isSubscriptionCancelled()) {
+ throw new EntitlementApiException(ErrorCode.SUB_UNCANCEL_BAD_STATE, getId());
+ }
+
+ final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
+ final Collection<BlockingState> pendingEntitlementCancellationEvents = eventsStream.getPendingEntitlementCancellationEvents();
+ if (eventsStream.isEntitlementCancelled()) {
+ final BlockingState cancellationEvent = eventsStream.getEntitlementCancellationEvent();
+ blockingStateDao.unactiveBlockingState(cancellationEvent.getId(), contextWithValidAccountRecordId);
+ } else if (pendingEntitlementCancellationEvents.size() > 0) {
+ // Reactivate entitlements
+ // See also https://github.com/killbill/killbill/issues/111
+ //
+ // Today we only support cancellation at SUBSCRIPTION level (Not ACCOUNT or BUNDLE), so we should really have only
+ // one future event in the list
+ //
+ for (final BlockingState futureCancellation : pendingEntitlementCancellationEvents) {
+ blockingStateDao.unactiveBlockingState(futureCancellation.getId(), contextWithValidAccountRecordId);
+ }
+ } else {
+ // Entitlement is NOT cancelled (or future cancelled), there is nothing to do
+ throw new EntitlementApiException(ErrorCode.SUB_UNCANCEL_BAD_STATE, getId());
+ }
+
+ // If billing was previously cancelled, reactivate
+ if (getSubscriptionBase().getFutureEndDate() != null) {
+ try {
+ getSubscriptionBase().uncancel(callContext);
+ } catch (final SubscriptionBaseApiException e) {
+ throw new EntitlementApiException(e);
+ }
+ }
+ return null;
}
- }
+ };
+
+ pluginExecution.executeWithPlugin(uncancelEntitlementWithPlugin, pluginContext);
}
@Override
@@ -391,7 +425,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
// See also EntitlementInternalApi#cancel for the bulk API
@Override
- public Entitlement cancelEntitlementWithDateOverrideBillingPolicy(final LocalDate localCancelDate, final BillingActionPolicy billingPolicy, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+ public Entitlement cancelEntitlementWithDateOverrideBillingPolicy(@Nullable final LocalDate entitlementEffectiveDate, final BillingActionPolicy billingPolicy, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
checkForPermissions(Permission.ENTITLEMENT_CAN_CANCEL, callContext);
@@ -404,7 +438,9 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
getBundleId(),
getExternalKey(),
null,
- localCancelDate,
+ entitlementEffectiveDate,
+ null,
+ billingPolicy,
properties,
callContext);
@@ -419,8 +455,6 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
// (we don't want an entitlement cancel date one second or so after the subscription cancel date or add-ons cancellations
// computations won't work).
final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
- final LocalDate effectiveLocalDate = new LocalDate(updatedPluginContext.getEffectiveDate(), eventsStream.getAccountTimeZone());
- final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(effectiveLocalDate, getSubscriptionBase().getStartDate(), contextWithValidAccountRecordId);
try {
// Cancel subscription base first, to correctly compute the add-ons entitlements we need to cancel (see below)
@@ -429,14 +463,15 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
throw new EntitlementApiException(e);
}
- final BlockingState newBlockingState = new DefaultBlockingState(getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_CANCELLED, EntitlementService.ENTITLEMENT_SERVICE_NAME, true, true, false, effectiveDate);
+ final DateTime entitlementEffectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEntitlementEffectiveDate(), contextWithValidAccountRecordId);
+ final BlockingState newBlockingState = new DefaultBlockingState(getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_CANCELLED, EntitlementService.ENTITLEMENT_SERVICE_NAME, true, true, false, entitlementEffectiveDate);
final Collection<NotificationEvent> notificationEvents = new ArrayList<NotificationEvent>();
- final Collection<BlockingState> addOnsBlockingStates = computeAddOnBlockingStates(effectiveDate, notificationEvents, callContext, contextWithValidAccountRecordId);
+ final Collection<BlockingState> addOnsBlockingStates = computeAddOnBlockingStates(entitlementEffectiveDate, notificationEvents, callContext, contextWithValidAccountRecordId);
// Record the new state first, then insert the notifications to avoid race conditions
setBlockingStates(newBlockingState, addOnsBlockingStates, contextWithValidAccountRecordId);
for (final NotificationEvent notificationEvent : notificationEvents) {
- recordFutureNotification(effectiveDate, notificationEvent, contextWithValidAccountRecordId);
+ recordFutureNotification(entitlementEffectiveDate, notificationEvent, contextWithValidAccountRecordId);
}
return entitlementApi.getEntitlementForId(getId(), callContext);
@@ -449,10 +484,14 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
final LocalDate cancellationDate;
switch (entitlementPolicy) {
case IMMEDIATE:
- cancellationDate = new LocalDate(clock.getUTCNow(), eventsStream.getAccountTimeZone());
+ cancellationDate = clock.getToday(eventsStream.getAccountTimeZone());
break;
case END_OF_TERM:
- cancellationDate = getSubscriptionBase().getChargedThroughDate() != null ? new LocalDate(getSubscriptionBase().getChargedThroughDate(), eventsStream.getAccountTimeZone()) : new LocalDate(clock.getUTCNow(), eventsStream.getAccountTimeZone());
+ if (getSubscriptionBase().getChargedThroughDate() != null) {
+ cancellationDate = internalTenantContext.toLocalDate(getSubscriptionBase().getChargedThroughDate());
+ } else {
+ cancellationDate = clock.getToday(eventsStream.getAccountTimeZone());
+ }
break;
default:
throw new RuntimeException("Unsupported policy " + entitlementPolicy);
@@ -476,6 +515,8 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
getExternalKey(),
null,
null,
+ null,
+ null,
properties,
callContext);
@@ -522,7 +563,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
}
@Override
- public Entitlement changePlanWithDate(final String productName, final BillingPeriod billingPeriod, final String priceList, final List<PlanPhasePriceOverride> overrides, final LocalDate localDate, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+ public Entitlement changePlanWithDate(final String productName, final BillingPeriod billingPeriod, final String priceList, final List<PlanPhasePriceOverride> overrides, @Nullable final LocalDate effectiveDate, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
checkForPermissions(Permission.ENTITLEMENT_CAN_CHANGE_PLAN, callContext);
@@ -535,7 +576,9 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
getBundleId(),
getExternalKey(),
null,
- localDate,
+ effectiveDate,
+ effectiveDate,
+ null,
properties,
callContext);
@@ -547,34 +590,34 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
}
final InternalCallContext context = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
- final DateTime effectiveChangeDateComputed = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), getSubscriptionBase().getStartDate(), context);
+ final DateTime effectiveChangeDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getBillingEffectiveDate(), context);
- final DateTime effectiveChangeDate;
+ final DateTime resultingEffectiveDate;
try {
- effectiveChangeDate = subscriptionInternalApi.getDryRunChangePlanEffectiveDate(getSubscriptionBase(), productName, billingPeriod, priceList, effectiveChangeDateComputed, null, context);
+ resultingEffectiveDate = subscriptionInternalApi.getDryRunChangePlanEffectiveDate(getSubscriptionBase(), productName, billingPeriod, priceList, effectiveChangeDate, null, context);
} catch (final SubscriptionBaseApiException e) {
throw new EntitlementApiException(e, e.getCode(), e.getMessage());
}
try {
- checker.checkBlockedChange(getSubscriptionBase(), effectiveChangeDate, context);
+ checker.checkBlockedChange(getSubscriptionBase(), resultingEffectiveDate, context);
} catch (final BlockingApiException e) {
throw new EntitlementApiException(e, e.getCode(), e.getMessage());
}
try {
- getSubscriptionBase().changePlanWithDate(productName, billingPeriod, priceList, overrides, effectiveChangeDate, callContext);
+ getSubscriptionBase().changePlanWithDate(productName, billingPeriod, priceList, overrides, resultingEffectiveDate, callContext);
} catch (final SubscriptionBaseApiException e) {
throw new EntitlementApiException(e);
}
final Collection<NotificationEvent> notificationEvents = new ArrayList<NotificationEvent>();
- final Iterable<BlockingState> addOnsBlockingStates = computeAddOnBlockingStates(effectiveChangeDate, notificationEvents, callContext, context);
+ final Iterable<BlockingState> addOnsBlockingStates = computeAddOnBlockingStates(resultingEffectiveDate, notificationEvents, callContext, context);
// Record the new state first, then insert the notifications to avoid race conditions
setBlockingStates(addOnsBlockingStates, context);
for (final NotificationEvent notificationEvent : notificationEvents) {
- recordFutureNotification(effectiveChangeDate, notificationEvent, context);
+ recordFutureNotification(resultingEffectiveDate, notificationEvent, context);
}
return entitlementApi.getEntitlementForId(getId(), callContext);
@@ -584,7 +627,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
}
@Override
- public Entitlement changePlanOverrideBillingPolicy(final String productName, final BillingPeriod billingPeriod, final String priceList, final List<PlanPhasePriceOverride> overrides, final LocalDate localDate, final BillingActionPolicy actionPolicy, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+ public Entitlement changePlanOverrideBillingPolicy(final String productName, final BillingPeriod billingPeriod, final String priceList, final List<PlanPhasePriceOverride> overrides, final LocalDate entitlementEffectiveDate, final BillingActionPolicy actionPolicy, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
checkForPermissions(Permission.ENTITLEMENT_CAN_CHANGE_PLAN, callContext);
@@ -597,7 +640,9 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
getBundleId(),
getExternalKey(),
null,
- localDate,
+ entitlementEffectiveDate,
+ null,
+ actionPolicy,
properties,
callContext);
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
index 0560b0e..c004046 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
@@ -24,6 +24,7 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
+import javax.annotation.Nullable;
import javax.inject.Inject;
import org.joda.time.DateTime;
@@ -72,6 +73,7 @@ import com.google.common.collect.Lists;
public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements EntitlementApi {
+ public static final String ENT_STATE_START = "ENT_STARTED";
public static final String ENT_STATE_BLOCKED = "ENT_BLOCKED";
public static final String ENT_STATE_CLEAR = "ENT_CLEAR";
public static final String ENT_STATE_CANCELLED = "ENT_CANCELLED";
@@ -111,11 +113,13 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
this.entitlementUtils = entitlementUtils;
this.pluginExecution = pluginExecution;
this.securityApi = securityApi;
- this.dateHelper = new EntitlementDateHelper(accountApi, clock);
+ this.dateHelper = new EntitlementDateHelper(clock);
}
@Override
- public Entitlement createBaseEntitlement(final UUID accountId, final PlanPhaseSpecifier planPhaseSpecifier, final String externalKey, final List<PlanPhasePriceOverride> overrides, final LocalDate effectiveDate, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+ public Entitlement createBaseEntitlement(final UUID accountId, final PlanPhaseSpecifier planPhaseSpecifier, final String externalKey, final List<PlanPhasePriceOverride> overrides,
+ @Nullable final LocalDate entitlementEffectiveDate, @Nullable LocalDate billingEffectiveDate, final boolean isMigrated,
+ final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
final EntitlementSpecifier entitlementSpecifier = new DefaultEntitlementSpecifier(planPhaseSpecifier, overrides);
final List<EntitlementSpecifier> entitlementSpecifierList = new ArrayList<EntitlementSpecifier>();
@@ -126,7 +130,9 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
null,
externalKey,
entitlementSpecifierList,
- effectiveDate,
+ entitlementEffectiveDate,
+ billingEffectiveDate,
+ null,
properties,
callContext);
@@ -135,19 +141,21 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
public Entitlement doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(accountId, callContext);
try {
-
if (entitlementUtils.getFirstActiveSubscriptionIdForKeyOrNull(externalKey, contextWithValidAccountRecordId) != null) {
throw new EntitlementApiException(new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_ACTIVE_BUNDLE_KEY_EXISTS, externalKey));
}
final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.createBundleForAccount(accountId, externalKey, contextWithValidAccountRecordId);
- final DateTime referenceTime = clock.getUTCNow();
- final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), referenceTime, contextWithValidAccountRecordId);
+ final DateTime billingRequestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getBillingEffectiveDate(), contextWithValidAccountRecordId);
final EntitlementSpecifier specifier = getFirstEntitlementSpecifier(updatedPluginContext.getEntitlementSpecifiers());
- final SubscriptionBase subscription = subscriptionBaseInternalApi.createSubscription(bundle.getId(), specifier.getPlanPhaseSpecifier(), specifier.getOverrides(), requestedDate, contextWithValidAccountRecordId);
+ final SubscriptionBase subscription = subscriptionBaseInternalApi.createSubscription(bundle.getId(), specifier.getPlanPhaseSpecifier(), specifier.getOverrides(), billingRequestedDate, isMigrated, contextWithValidAccountRecordId);
- return new DefaultEntitlement(subscription.getId(), eventsStreamBuilder, entitlementApi, pluginExecution,
+ final DateTime entitlementRequestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEntitlementEffectiveDate(), contextWithValidAccountRecordId);
+ final BlockingState newBlockingState = new DefaultBlockingState(subscription.getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_START, EntitlementService.ENTITLEMENT_SERVICE_NAME, false, false, false, entitlementRequestedDate);
+ entitlementUtils.setBlockingStatesAndPostBlockingTransitionEvent(ImmutableList.<BlockingState>of(newBlockingState), subscription.getBundleId(), contextWithValidAccountRecordId);
+
+ return new DefaultEntitlement(accountId, subscription.getId(), eventsStreamBuilder, entitlementApi, pluginExecution,
blockingStateDao, subscriptionBaseInternalApi, checker, notificationQueueService,
entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory, callContext);
} catch (final SubscriptionBaseApiException e) {
@@ -167,7 +175,8 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
@Override
public Entitlement createBaseEntitlementWithAddOns(final UUID accountId, final String externalKey, final Iterable<EntitlementSpecifier> entitlementSpecifiers,
- final LocalDate effectiveDate, final Iterable<PluginProperty> properties, final CallContext callContext)
+ @Nullable LocalDate entitlementEffectiveDate, @Nullable LocalDate billingEffectiveDate, final boolean isMigrated,
+ final Iterable<PluginProperty> properties, final CallContext callContext)
throws EntitlementApiException {
final EntitlementSpecifier baseSpecifier = Iterables.tryFind(entitlementSpecifiers, new Predicate<EntitlementSpecifier>() {
@@ -190,7 +199,9 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
null,
externalKey,
entitlementSpecifierList,
- effectiveDate,
+ entitlementEffectiveDate,
+ billingEffectiveDate,
+ null,
properties,
callContext);
@@ -206,11 +217,20 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.createBundleForAccount(accountId, externalKey, contextWithValidAccountRecordId);
- final DateTime referenceTime = clock.getUTCNow();
- final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), referenceTime, contextWithValidAccountRecordId);
- final SubscriptionBase subscription = subscriptionBaseInternalApi.createBaseSubscriptionWithAddOns(bundle.getId(), entitlementSpecifiers, requestedDate, contextWithValidAccountRecordId);
+ final DateTime billingRequestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getBillingEffectiveDate(), contextWithValidAccountRecordId);
+ final List<SubscriptionBase> subscriptionBases = subscriptionBaseInternalApi.createBaseSubscriptionWithAddOns(bundle.getId(), entitlementSpecifiers, billingRequestedDate, isMigrated, contextWithValidAccountRecordId);
- return new DefaultEntitlement(subscription.getId(), eventsStreamBuilder, entitlementApi, pluginExecution,
+ final DateTime entitlementRequestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEntitlementEffectiveDate(), contextWithValidAccountRecordId);
+ final List<BlockingState> blockingStates = new ArrayList<BlockingState>();
+ for (final SubscriptionBase cur : subscriptionBases) {
+ final BlockingState blockingState = new DefaultBlockingState(cur.getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_START, EntitlementService.ENTITLEMENT_SERVICE_NAME, false, false, false, entitlementRequestedDate);
+ blockingStates.add(blockingState);
+ }
+ final SubscriptionBase subscriptionBaseBP = subscriptionBases.get(0);
+ entitlementUtils.setBlockingStatesAndPostBlockingTransitionEvent(blockingStates, subscriptionBaseBP.getBundleId(), contextWithValidAccountRecordId);
+
+
+ return new DefaultEntitlement(accountId, subscriptionBaseBP.getId(), eventsStreamBuilder, entitlementApi, pluginExecution,
blockingStateDao, subscriptionBaseInternalApi, checker, notificationQueueService,
entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory, callContext);
@@ -224,7 +244,8 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
}
@Override
- public Entitlement addEntitlement(final UUID bundleId, final PlanPhaseSpecifier planPhaseSpecifier, final List<PlanPhasePriceOverride> overrides, final LocalDate effectiveDate, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+ public Entitlement addEntitlement(final UUID bundleId, final PlanPhaseSpecifier planPhaseSpecifier, final List<PlanPhasePriceOverride> overrides, @Nullable final LocalDate entitlementEffectiveDate, @Nullable final LocalDate billingEffectiveDate,
+ final boolean isMigrated, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
final EntitlementSpecifier entitlementSpecifier = new DefaultEntitlementSpecifier(planPhaseSpecifier, overrides);
final List<EntitlementSpecifier> entitlementSpecifierList = new ArrayList<EntitlementSpecifier>();
@@ -235,7 +256,9 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
bundleId,
null,
entitlementSpecifierList,
- effectiveDate,
+ entitlementEffectiveDate,
+ billingEffectiveDate,
+ null,
properties,
callContext);
@@ -254,14 +277,18 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
throw new EntitlementApiException(new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, BlockingChecker.ACTION_CHANGE, BlockingChecker.TYPE_SUBSCRIPTION, eventsStreamForBaseSubscription.getEntitlementId().toString()));
}
- final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), eventsStreamForBaseSubscription.getSubscriptionBase().getStartDate(), eventsStreamForBaseSubscription.getInternalTenantContext());
-
+ final DateTime billingRequestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getBillingEffectiveDate(), eventsStreamForBaseSubscription.getInternalTenantContext());
try {
- final InternalCallContext context = internalCallContextFactory.createInternalCallContext(callContext);
+ final InternalCallContext context = internalCallContextFactory.createInternalCallContext(eventsStreamForBaseSubscription.getAccountId(), callContext);
final EntitlementSpecifier specifier = getFirstEntitlementSpecifier(updatedPluginContext.getEntitlementSpecifiers());
- final SubscriptionBase subscription = subscriptionBaseInternalApi.createSubscription(bundleId, specifier.getPlanPhaseSpecifier(), specifier.getOverrides(), requestedDate, context);
+ final SubscriptionBase subscription = subscriptionBaseInternalApi.createSubscription(bundleId, specifier.getPlanPhaseSpecifier(), specifier.getOverrides(), billingRequestedDate, isMigrated, context);
- return new DefaultEntitlement(subscription.getId(), eventsStreamBuilder, entitlementApi, pluginExecution,
+ final DateTime entitlementRequestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEntitlementEffectiveDate(), eventsStreamForBaseSubscription.getInternalTenantContext());
+ final BlockingState newBlockingState = new DefaultBlockingState(subscription.getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_START, EntitlementService.ENTITLEMENT_SERVICE_NAME, false, false, false, entitlementRequestedDate);
+ entitlementUtils.setBlockingStatesAndPostBlockingTransitionEvent(ImmutableList.<BlockingState>of(newBlockingState), subscription.getBundleId(), context);
+
+
+ return new DefaultEntitlement(eventsStreamForBaseSubscription.getAccountId(), subscription.getId(), eventsStreamBuilder, entitlementApi, pluginExecution,
blockingStateDao, subscriptionBaseInternalApi, checker, notificationQueueService,
entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory, callContext);
} catch (final SubscriptionBaseApiException e) {
@@ -273,14 +300,14 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
}
@Override
- public List<EntitlementAOStatusDryRun> getDryRunStatusForChange(final UUID bundleId, final String targetProductName, final LocalDate effectiveDate, final TenantContext context) throws EntitlementApiException {
- final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
+ public List<EntitlementAOStatusDryRun> getDryRunStatusForChange(final UUID bundleId, final String targetProductName, @Nullable final LocalDate effectiveDate, final TenantContext context) throws EntitlementApiException {
+ final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(bundleId, ObjectType.BUNDLE, context);
try {
final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.getBundleFromId(bundleId, internalContext);
final SubscriptionBase baseSubscription = subscriptionBaseInternalApi.getBaseSubscription(bundleId, internalContext);
final InternalTenantContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalTenantContext(bundle.getAccountId(), context);
- final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(effectiveDate, baseSubscription.getStartDate(), contextWithValidAccountRecordId);
+ final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(effectiveDate, contextWithValidAccountRecordId);
return subscriptionBaseInternalApi.getDryRunChangePlanStatus(baseSubscription.getId(), targetProductName, requestedDate, contextWithValidAccountRecordId);
} catch (final SubscriptionBaseApiException e) {
throw new EntitlementApiException(e);
@@ -295,7 +322,7 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
@Override
public List<Entitlement> getAllEntitlementsForBundle(final UUID bundleId, final TenantContext tenantContext) throws EntitlementApiException {
- final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(tenantContext);
+ final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(bundleId, ObjectType.BUNDLE, tenantContext);
final UUID accountId;
try {
accountId = subscriptionBaseInternalApi.getBundleFromId(bundleId, internalContext).getAccountId();
@@ -314,7 +341,7 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
@Override
public List<Entitlement> getAllEntitlementsForAccountIdAndExternalKey(final UUID accountId, final String externalKey, final TenantContext tenantContext) throws EntitlementApiException {
- // getAllEntitlementsForAccountId should be fast (uses account_record_id)
+ // getAllEntitlementsForAccount should be fast (uses account_record_id)
return ImmutableList.<Entitlement>copyOf(Iterables.<Entitlement>filter(getAllEntitlementsForAccountId(accountId, tenantContext),
new Predicate<Entitlement>() {
@Override
@@ -337,7 +364,7 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
public Entitlement apply(final EventsStream eventsStream) {
return new DefaultEntitlement(eventsStream, eventsStreamBuilder, entitlementApi, pluginExecution,
blockingStateDao, subscriptionBaseInternalApi, checker, notificationQueueService,
- entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory);
+ entitlementUtils, dateHelper, clock, securityApi, context, internalCallContextFactory);
}
});
}
@@ -376,7 +403,7 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
}
@Override
- public UUID transferEntitlementsOverrideBillingPolicy(final UUID sourceAccountId, final UUID destAccountId, final String externalKey, final LocalDate effectiveDate, final BillingActionPolicy billingPolicy, final Iterable<PluginProperty> properties, final CallContext context) throws EntitlementApiException {
+ public UUID transferEntitlementsOverrideBillingPolicy(final UUID sourceAccountId, final UUID destAccountId, final String externalKey, @Nullable final LocalDate effectiveDate, final BillingActionPolicy billingPolicy, final Iterable<PluginProperty> properties, final CallContext context) throws EntitlementApiException {
final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.TRANSFER_BUNDLE,
sourceAccountId,
@@ -385,6 +412,8 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
externalKey,
new ArrayList<EntitlementSpecifier>(),
effectiveDate,
+ effectiveDate,
+ null,
properties,
context);
@@ -403,30 +432,44 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
throw new RuntimeException("Unexpected billing policy " + billingPolicy);
}
- final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(sourceAccountId, context);
+ final InternalCallContext contextWithSourceAccountRecordId = internalCallContextFactory.createInternalCallContext(sourceAccountId, context);
try {
- final UUID activeSubscriptionIdForKey = entitlementUtils.getFirstActiveSubscriptionIdForKeyOrNull(externalKey, contextWithValidAccountRecordId);
+ final UUID activeSubscriptionIdForKey = entitlementUtils.getFirstActiveSubscriptionIdForKeyOrNull(externalKey, contextWithSourceAccountRecordId);
final SubscriptionBase baseSubscription = activeSubscriptionIdForKey != null ?
- subscriptionBaseInternalApi.getSubscriptionFromId(activeSubscriptionIdForKey, contextWithValidAccountRecordId) : null;
+ subscriptionBaseInternalApi.getSubscriptionFromId(activeSubscriptionIdForKey, contextWithSourceAccountRecordId) : null;
final SubscriptionBaseBundle baseBundle = baseSubscription != null ?
- subscriptionBaseInternalApi.getBundleFromId(baseSubscription.getBundleId(), contextWithValidAccountRecordId) : null;
+ subscriptionBaseInternalApi.getBundleFromId(baseSubscription.getBundleId(), contextWithSourceAccountRecordId) : null;
if (baseBundle == null || !baseBundle.getAccountId().equals(sourceAccountId)) {
throw new EntitlementApiException(new SubscriptionBaseApiException(ErrorCode.SUB_GET_INVALID_BUNDLE_KEY, externalKey));
}
- final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), baseSubscription.getStartDate(), contextWithValidAccountRecordId);
+ final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getBillingEffectiveDate(), contextWithSourceAccountRecordId);
final SubscriptionBaseBundle newBundle = subscriptionBaseTransferApi.transferBundle(sourceAccountId, destAccountId, externalKey, requestedDate, true, cancelImm, context);
+
+ final Map<BlockingState, UUID> blockingStates = new HashMap<BlockingState, UUID>();
+
// Block all associated subscriptions - TODO Do we want to block the bundle as well (this will add an extra STOP_ENTITLEMENT event in the bundle timeline stream)?
// Note that there is no un-transfer at the moment, so we effectively add a blocking state on disk for all subscriptions
- final Map<BlockingState, UUID> blockingStates = new HashMap<BlockingState, UUID>();
- for (final SubscriptionBase subscriptionBase : subscriptionBaseInternalApi.getSubscriptionsForBundle(baseBundle.getId(), null, contextWithValidAccountRecordId)) {
+ for (final SubscriptionBase subscriptionBase : subscriptionBaseInternalApi.getSubscriptionsForBundle(baseBundle.getId(), null, contextWithSourceAccountRecordId)) {
final BlockingState blockingState = new DefaultBlockingState(subscriptionBase.getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_CANCELLED, EntitlementService.ENTITLEMENT_SERVICE_NAME, true, true, false, requestedDate);
blockingStates.put(blockingState, subscriptionBase.getBundleId());
}
- entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingStates, contextWithValidAccountRecordId);
+ entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingStates, contextWithSourceAccountRecordId);
+
+ // Add blocking events for transferred subscriptions..
+ final InternalCallContext contextWithDestAccountRecordId = internalCallContextFactory.createInternalCallContext(destAccountId, context);
+
+ blockingStates.clear();
+ final DateTime entitlementRequestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEntitlementEffectiveDate(), contextWithDestAccountRecordId);
+ for (final SubscriptionBase subscriptionBase : subscriptionBaseInternalApi.getSubscriptionsForBundle(newBundle.getId(), null, contextWithDestAccountRecordId)) {
+ final BlockingState newBlockingState = new DefaultBlockingState(subscriptionBase.getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_START, EntitlementService.ENTITLEMENT_SERVICE_NAME, false, false, false, entitlementRequestedDate);
+ blockingStates.put(newBlockingState, subscriptionBase.getBundleId());
+ }
+ entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingStates, contextWithDestAccountRecordId);
+
return newBundle.getId();
} catch (final SubscriptionBaseTransferApiException e) {
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementContext.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementContext.java
index 79da30c..d5ec969 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementContext.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementContext.java
@@ -24,6 +24,7 @@ import javax.annotation.Nullable;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
+import org.killbill.billing.catalog.api.BillingActionPolicy;
import org.killbill.billing.entitlement.plugin.api.EntitlementContext;
import org.killbill.billing.entitlement.plugin.api.OperationType;
import org.killbill.billing.entitlement.plugin.api.PriorEntitlementResult;
@@ -41,7 +42,9 @@ public class DefaultEntitlementContext implements EntitlementContext {
private final UUID bundleId;
private final String externalKey;
private final List<EntitlementSpecifier> entitlementSpecifiers;
- private final LocalDate effectiveDate;
+ private final LocalDate entitlementEffectiveDate;
+ private final LocalDate billingEffectiveDate;
+ private final BillingActionPolicy billingActionPolicy;
private final Iterable<PluginProperty> pluginProperties;
private final UUID userToken;
private final String userName;
@@ -62,7 +65,9 @@ public class DefaultEntitlementContext implements EntitlementContext {
prev.getBundleId(),
prev.getExternalKey(),
pluginResult != null && pluginResult.getAdjustedEntitlementSpecifiers() != null ? pluginResult.getAdjustedEntitlementSpecifiers() : prev.getEntitlementSpecifiers(),
- pluginResult != null && pluginResult.getAdjustedEffectiveDate() != null ? pluginResult.getAdjustedEffectiveDate() : prev.getEffectiveDate(),
+ pluginResult != null && pluginResult.getAdjustedEntitlementEffectiveDate() != null ? pluginResult.getAdjustedEntitlementEffectiveDate() : prev.getEntitlementEffectiveDate(),
+ pluginResult != null && pluginResult.getAdjustedBillingEffectiveDate() != null ? pluginResult.getAdjustedBillingEffectiveDate() : prev.getBillingEffectiveDate(),
+ pluginResult != null && pluginResult.getAdjustedBillingActionPolicy() != null ? pluginResult.getAdjustedBillingActionPolicy() : prev.getBillingActionPolicy(),
pluginResult != null && pluginResult.getAdjustedPluginProperties() != null ? pluginResult.getAdjustedPluginProperties() : prev.getPluginProperties(),
prev);
}
@@ -73,10 +78,12 @@ public class DefaultEntitlementContext implements EntitlementContext {
final UUID bundleId,
final String externalKey,
final List<EntitlementSpecifier> entitlementSpecifiers,
- final LocalDate effectiveDate,
+ @Nullable final LocalDate entitlementEffectiveDate,
+ @Nullable final LocalDate billingEffectiveDate,
+ @Nullable final BillingActionPolicy actionPolicy,
final Iterable<PluginProperty> pluginProperties,
final CallContext callContext) {
- this(operationType, accountId, destinationAccountId, bundleId, externalKey, entitlementSpecifiers, effectiveDate, pluginProperties,
+ this(operationType, accountId, destinationAccountId, bundleId, externalKey, entitlementSpecifiers, entitlementEffectiveDate, billingEffectiveDate, actionPolicy, pluginProperties,
callContext.getUserToken(), callContext.getUserName(), callContext.getCallOrigin(), callContext.getUserType(), callContext.getReasonCode(),
callContext.getComments(), callContext.getCreatedDate(), callContext.getUpdatedDate(), callContext.getTenantId());
}
@@ -88,7 +95,9 @@ public class DefaultEntitlementContext implements EntitlementContext {
final UUID bundleId,
final String externalKey,
final List<EntitlementSpecifier> entitlementSpecifiers,
- final LocalDate effectiveDate,
+ @Nullable final LocalDate entitlementEffectiveDate,
+ @Nullable final LocalDate billingEffectiveDate,
+ @Nullable final BillingActionPolicy actionPolicy,
final Iterable<PluginProperty> pluginProperties,
final UUID userToken,
final String userName,
@@ -105,7 +114,9 @@ public class DefaultEntitlementContext implements EntitlementContext {
this.bundleId = bundleId;
this.externalKey = externalKey;
this.entitlementSpecifiers = entitlementSpecifiers;
- this.effectiveDate = effectiveDate;
+ this.entitlementEffectiveDate = entitlementEffectiveDate;
+ this.billingEffectiveDate = billingEffectiveDate;
+ this.billingActionPolicy = actionPolicy;
this.pluginProperties = pluginProperties;
this.userToken = userToken;
this.userName = userName;
@@ -148,9 +159,20 @@ public class DefaultEntitlementContext implements EntitlementContext {
return entitlementSpecifiers;
}
+
+ @Override
+ public LocalDate getEntitlementEffectiveDate() {
+ return entitlementEffectiveDate;
+ }
+
+ @Override
+ public LocalDate getBillingEffectiveDate() {
+ return billingEffectiveDate;
+ }
+
@Override
- public LocalDate getEffectiveDate() {
- return effectiveDate;
+ public BillingActionPolicy getBillingActionPolicy() {
+ return billingActionPolicy;
}
@Override
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscription.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscription.java
index 3667835..0d7dc82 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscription.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscription.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -31,7 +31,7 @@ public class DefaultSubscription extends DefaultEntitlement implements Subscript
@Override
public LocalDate getBillingStartDate() {
- return new LocalDate(getSubscriptionBase().getStartDate(), getAccountTimeZone());
+ return internalTenantContext.toLocalDate(getSubscriptionBase().getStartDate());
}
@Override
@@ -51,16 +51,16 @@ public class DefaultSubscription extends DefaultEntitlement implements Subscript
futureOrCurrentEndDate = futureOrCurrentEndDateForSubscription;
}
- return futureOrCurrentEndDate != null ? new LocalDate(futureOrCurrentEndDate, getAccountTimeZone()) : null;
+ return futureOrCurrentEndDate != null ? internalTenantContext.toLocalDate(futureOrCurrentEndDate) : null;
}
@Override
public LocalDate getChargedThroughDate() {
- return getSubscriptionBase().getChargedThroughDate() != null ? new LocalDate(getSubscriptionBase().getChargedThroughDate(), getAccountTimeZone()) : null;
+ return getSubscriptionBase().getChargedThroughDate() != null ? internalTenantContext.toLocalDate(getSubscriptionBase().getChargedThroughDate()) : null;
}
@Override
public List<SubscriptionEvent> getSubscriptionEvents() {
- return SubscriptionEventOrdering.sortedCopy(this, getAccountTimeZone());
+ return SubscriptionEventOrdering.sortedCopy(this, internalTenantContext);
}
}
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
index 5aeffe1..fc63c7b 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -26,16 +26,27 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
+import javax.annotation.Nullable;
import javax.inject.Inject;
-import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
import org.killbill.billing.ErrorCode;
import org.killbill.billing.ObjectType;
+import org.killbill.billing.OrderingType;
+import org.killbill.billing.account.api.AccountApiException;
+import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.account.api.ImmutableAccountData;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.entitlement.AccountEntitlements;
import org.killbill.billing.entitlement.EntitlementInternalApi;
+import org.killbill.billing.entitlement.EntitlementService;
+import org.killbill.billing.entitlement.api.EntitlementPluginExecution.WithEntitlementPlugin;
+import org.killbill.billing.entitlement.dao.BlockingStateDao;
import org.killbill.billing.entitlement.engine.core.EntitlementUtils;
+import org.killbill.billing.entitlement.plugin.api.EntitlementContext;
+import org.killbill.billing.entitlement.plugin.api.OperationType;
+import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.subscription.api.SubscriptionBase;
import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
@@ -46,6 +57,7 @@ import org.killbill.billing.util.callcontext.TenantContext;
import org.killbill.billing.util.customfield.ShouldntHappenException;
import org.killbill.billing.util.entity.Pagination;
import org.killbill.billing.util.entity.dao.DefaultPaginationHelper.SourcePaginationBuilder;
+import org.killbill.clock.Clock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,6 +66,7 @@ import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import static org.killbill.billing.util.entity.dao.DefaultPaginationHelper.getEntityPaginationNoException;
@@ -80,17 +93,32 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
}
};
+ private final AccountInternalApi accountApi;
private final EntitlementInternalApi entitlementInternalApi;
private final SubscriptionBaseInternalApi subscriptionBaseInternalApi;
private final InternalCallContextFactory internalCallContextFactory;
private final EntitlementUtils entitlementUtils;
+ private final Clock clock;
+ private final EntitlementPluginExecution pluginExecution;
+ private final BlockingStateDao blockingStateDao;
+
@Inject
- public DefaultSubscriptionApi(final EntitlementInternalApi entitlementInternalApi, final SubscriptionBaseInternalApi subscriptionInternalApi,
- final InternalCallContextFactory internalCallContextFactory, final EntitlementUtils entitlementUtils) {
+ public DefaultSubscriptionApi(final AccountInternalApi accountApi,
+ final EntitlementInternalApi entitlementInternalApi,
+ final SubscriptionBaseInternalApi subscriptionInternalApi,
+ final InternalCallContextFactory internalCallContextFactory,
+ final Clock clock,
+ final EntitlementPluginExecution pluginExecution,
+ final BlockingStateDao blockingStateDao,
+ final EntitlementUtils entitlementUtils) {
+ this.accountApi = accountApi;
this.entitlementInternalApi = entitlementInternalApi;
this.subscriptionBaseInternalApi = subscriptionInternalApi;
this.internalCallContextFactory = internalCallContextFactory;
+ this.clock = clock;
+ this.pluginExecution = pluginExecution;
+ this.blockingStateDao = blockingStateDao;
this.entitlementUtils = entitlementUtils;
}
@@ -102,7 +130,7 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
try {
final UUID accountId = internalCallContextFactory.getAccountId(entitlementId, ObjectType.SUBSCRIPTION, tenantContext);
final InternalTenantContext internalTenantContextWithValidAccountRecordId = internalCallContextFactory.createInternalTenantContext(accountId, tenantContext);
- accountEntitlements = entitlementInternalApi.getAllEntitlementsForAccountId(accountId, internalTenantContextWithValidAccountRecordId);
+ accountEntitlements = entitlementInternalApi.getAllEntitlementsForAccount(internalTenantContextWithValidAccountRecordId);
} catch (final EntitlementApiException e) {
throw new SubscriptionApiException(e);
}
@@ -121,10 +149,6 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
@Override
public SubscriptionBundle getSubscriptionBundle(final UUID bundleId, final TenantContext tenantContext) throws SubscriptionApiException {
-
- final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(tenantContext);
-
-
final UUID accountId = internalCallContextFactory.getAccountId(bundleId, ObjectType.BUNDLE, tenantContext);
final Optional<SubscriptionBundle> bundleOptional = Iterables.<SubscriptionBundle>tryFind(getSubscriptionBundlesForAccount(accountId, tenantContext),
@@ -142,12 +166,6 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
}
@Override
- public void updateExternalKey(final UUID uuid, final String newExternalKey, final CallContext callContext) {
- final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(callContext);
- subscriptionBaseInternalApi.updateExternalKey(uuid, newExternalKey, internalContext);
- }
-
- @Override
public List<SubscriptionBundle> getSubscriptionBundlesForAccountIdAndExternalKey(final UUID accountId, final String externalKey, final TenantContext context) throws SubscriptionApiException {
return ImmutableList.<SubscriptionBundle>copyOf(Iterables.<SubscriptionBundle>filter(getSubscriptionBundlesForAccount(accountId, context),
new Predicate<SubscriptionBundle>() {
@@ -160,7 +178,7 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
@Override
public SubscriptionBundle getActiveSubscriptionBundleForExternalKey(final String externalKey, final TenantContext context) throws SubscriptionApiException {
- final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
+ final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context);
try {
final UUID activeSubscriptionIdForKey = entitlementUtils.getFirstActiveSubscriptionIdForKeyOrNull(externalKey, internalContext);
if (activeSubscriptionIdForKey == null) {
@@ -175,7 +193,7 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
@Override
public List<SubscriptionBundle> getSubscriptionBundlesForExternalKey(final String externalKey, final TenantContext context) throws SubscriptionApiException {
- final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
+ final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context);
final List<SubscriptionBaseBundle> baseBundles = subscriptionBaseInternalApi.getBundlesForKey(externalKey, internalContext);
final List<SubscriptionBundle> result = new ArrayList<SubscriptionBundle>(baseBundles.size());
@@ -194,7 +212,7 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
@Override
public Pagination<SubscriptionBundle> getSubscriptionBundles(final Long offset, final Long limit, final TenantContext context) {
- final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
+ final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context);
return getEntityPaginationNoException(limit,
new SourcePaginationBuilder<SubscriptionBaseBundle, SubscriptionApiException>() {
@Override
@@ -218,7 +236,7 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
@Override
public Pagination<SubscriptionBundle> searchSubscriptionBundles(final String searchKey, final Long offset, final Long limit, final TenantContext context) {
- final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
+ final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context);
return getEntityPaginationNoException(limit,
new SourcePaginationBuilder<SubscriptionBaseBundle, SubscriptionApiException>() {
@Override
@@ -240,12 +258,183 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
);
}
+ @Override
+ public void updateExternalKey(final UUID bundleId, final String newExternalKey, final CallContext callContext) throws EntitlementApiException {
+ final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(callContext);
+
+ final SubscriptionBaseBundle bundle;
+ final ImmutableAccountData account;
+ try {
+ bundle = subscriptionBaseInternalApi.getBundleFromId(bundleId, internalCallContext);
+ account = accountApi.getImmutableAccountDataById(bundle.getAccountId(), internalCallContext);
+ } catch (final SubscriptionBaseApiException e) {
+ throw new EntitlementApiException(e);
+ } catch (AccountApiException e) {
+ throw new EntitlementApiException(e);
+ }
+
+ final LocalDate effectiveDate = new LocalDate(clock.getUTCNow(), account.getTimeZone());
+ final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.UPDATE_BUNDLE_EXTERNAL_KEY,
+ bundle.getAccountId(),
+ null,
+ bundleId,
+ newExternalKey,
+ new ArrayList<EntitlementSpecifier>(),
+ effectiveDate,
+ effectiveDate,
+ null,
+ ImmutableList.<PluginProperty>of(),
+ callContext);
+
+ final WithEntitlementPlugin<Void> updateExternalKeyWithPlugin = new WithEntitlementPlugin<Void>() {
+
+ final InternalCallContext internalCallContextWithValidAccountId = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+
+ @Override
+ public Void doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
+ subscriptionBaseInternalApi.updateExternalKey(bundleId, newExternalKey, internalCallContextWithValidAccountId);
+ return null;
+ }
+ };
+ pluginExecution.executeWithPlugin(updateExternalKeyWithPlugin, pluginContext);
+ }
+
+ @Override
+ public void addBlockingState(final BlockingState blockingState, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+
+ // This is in no way an exhaustive arg validation, but to to ensure plugin would not hijack private entitlement state or service name
+ if (blockingState.getService() == null || blockingState.getService().equals(EntitlementService.ENTITLEMENT_SERVICE_NAME)) {
+ throw new EntitlementApiException(ErrorCode.SUB_BLOCKING_STATE_INVALID_ARG, "Need to specify a valid serviceName");
+ }
+
+ if (blockingState.getStateName() == null ||
+ blockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CANCELLED) ||
+ blockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_BLOCKED) ||
+ blockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CLEAR)) {
+ throw new EntitlementApiException(ErrorCode.SUB_BLOCKING_STATE_INVALID_ARG, "Need to specify a valid stateName");
+ }
+
+ final InternalCallContext internalCallContextWithValidAccountId;
+ final ImmutableAccountData account;
+ final UUID accountId;
+ final UUID bundleId;
+ final String externalKey;
+ try {
+ switch (blockingState.getType()) {
+ case ACCOUNT:
+ internalCallContextWithValidAccountId = internalCallContextFactory.createInternalCallContext(blockingState.getBlockedId(), ObjectType.ACCOUNT, callContext);
+ account = accountApi.getImmutableAccountDataById(blockingState.getBlockedId(), internalCallContextWithValidAccountId);
+ externalKey = account.getExternalKey();
+ accountId = account.getId();
+ bundleId = null;
+ break;
+
+ case SUBSCRIPTION_BUNDLE:
+ internalCallContextWithValidAccountId = internalCallContextFactory.createInternalCallContext(blockingState.getBlockedId(), ObjectType.BUNDLE, callContext);
+ final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.getBundleFromId(blockingState.getBlockedId(), internalCallContextWithValidAccountId);
+ externalKey = bundle.getExternalKey();
+ bundleId = bundle.getId();
+ accountId = bundle.getAccountId();
+ account = accountApi.getImmutableAccountDataById(accountId, internalCallContextWithValidAccountId);
+ break;
+
+ case SUBSCRIPTION:
+ internalCallContextWithValidAccountId = internalCallContextFactory.createInternalCallContext(blockingState.getBlockedId(), ObjectType.SUBSCRIPTION, callContext);
+ final Entitlement entitlement = entitlementInternalApi.getEntitlementForId(blockingState.getBlockedId(), internalCallContextWithValidAccountId);
+ bundleId = entitlement.getBundleId();
+ accountId = entitlement.getAccountId();
+ account = accountApi.getImmutableAccountDataById(accountId, internalCallContextWithValidAccountId);
+ externalKey = null;
+ break;
+
+ default:
+ throw new IllegalStateException("Invalid blockingStateType " + blockingState.getType());
+ }
+ } catch (final AccountApiException e) {
+ throw new EntitlementApiException(e);
+ } catch (final SubscriptionBaseApiException e) {
+ throw new EntitlementApiException(e);
+ }
+
+
+
+ final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.ADD_BLOCKING_STATE,
+ accountId,
+ null,
+ bundleId,
+ externalKey,
+ new ArrayList<EntitlementSpecifier>(),
+ new LocalDate(blockingState.getEffectiveDate(), account.getTimeZone()),
+ null,
+ null,
+ properties,
+ callContext);
+
+
+ final WithEntitlementPlugin<Void> addBlockingStateWithPlugin = new WithEntitlementPlugin<Void>() {
+
+ @Override
+ public Void doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
+ entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingState, internalCallContextWithValidAccountId);
+ return null;
+ }
+ };
+ pluginExecution.executeWithPlugin(addBlockingStateWithPlugin, pluginContext);
+ }
+
+ @Override
+ public Iterable<BlockingState> getBlockingStates(final UUID accountId, @Nullable final List<BlockingStateType> typeFilter, @Nullable final List<String> svcsFilter, final OrderingType orderingType, final int timeFilter, final TenantContext tenantContext) throws EntitlementApiException {
+
+ try {
+
+ final InternalTenantContext internalTenantContextWithValidAccountRecordId = internalCallContextFactory.createInternalTenantContext(accountId, tenantContext);
+ final List<BlockingState> allBlockingStates = blockingStateDao.getBlockingAllForAccountRecordId(internalTenantContextWithValidAccountRecordId);
+
+ final ImmutableAccountData account = accountApi.getImmutableAccountDataById(accountId, internalTenantContextWithValidAccountRecordId);
+
+ final Iterable<BlockingState> filteredByTypes = typeFilter != null && !typeFilter.isEmpty() ?
+ Iterables.filter(allBlockingStates, new Predicate<BlockingState>() {
+ @Override
+ public boolean apply(final BlockingState input) {
+ return typeFilter.contains(input.getType());
+ }
+ }) : allBlockingStates;
+
+ final Iterable<BlockingState> filteredByTypesAndSvcs = svcsFilter != null && !svcsFilter.isEmpty() ?
+ Iterables.filter(filteredByTypes, new Predicate<BlockingState>() {
+ @Override
+ public boolean apply(final BlockingState input) {
+ return svcsFilter.contains(input.getService());
+ }
+ }) : filteredByTypes;
+
+ final LocalDate localDateNowInAccountTimezone = new LocalDate(clock.getUTCNow(), account.getTimeZone());
+ final List<BlockingState> result = new ArrayList<BlockingState>();
+ for (final BlockingState cur : filteredByTypesAndSvcs) {
+
+ final LocalDate eventDate = new LocalDate(cur.getEffectiveDate(), account.getTimeZone());
+ final int comp = eventDate.compareTo(localDateNowInAccountTimezone);
+ if ((comp <= 1 && ((timeFilter & SubscriptionApi.PAST_EVENTS) == SubscriptionApi.PAST_EVENTS)) ||
+ (comp == 0 && ((timeFilter & SubscriptionApi.PRESENT_EVENTS) == SubscriptionApi.PRESENT_EVENTS)) ||
+ (comp >= 1 && ((timeFilter & SubscriptionApi.FUTURE_EVENTS) == SubscriptionApi.FUTURE_EVENTS))) {
+ result.add(cur);
+ }
+ }
+
+ return orderingType == OrderingType.ASCENDING ? result : Lists.reverse(result);
+
+ } catch (AccountApiException e) {
+ throw new EntitlementApiException(e);
+ }
+ }
+
private List<SubscriptionBundle> getSubscriptionBundlesForAccount(final UUID accountId, final TenantContext tenantContext) throws SubscriptionApiException {
+ final InternalTenantContext internalTenantContextWithValidAccountRecordId = internalCallContextFactory.createInternalTenantContext(accountId, tenantContext);
+
// Retrieve entitlements
final AccountEntitlements accountEntitlements;
try {
- final InternalTenantContext internalTenantContextWithValidAccountRecordId = internalCallContextFactory.createInternalTenantContext(accountId, tenantContext);
- accountEntitlements = entitlementInternalApi.getAllEntitlementsForAccountId(accountId, internalTenantContextWithValidAccountRecordId);
+ accountEntitlements = entitlementInternalApi.getAllEntitlementsForAccount(internalTenantContextWithValidAccountRecordId);
} catch (final EntitlementApiException e) {
throw new SubscriptionApiException(e);
}
@@ -253,19 +442,17 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
// Build subscriptions
final Map<UUID, List<Subscription>> subscriptionsPerBundle = buildSubscriptionsFromEntitlements(accountEntitlements);
- final DateTimeZone accountTimeZone = accountEntitlements.getAccount().getTimeZone();
-
// Build subscription bundles
final List<SubscriptionBundle> bundles = new LinkedList<SubscriptionBundle>();
for (final UUID bundleId : subscriptionsPerBundle.keySet()) {
final List<Subscription> subscriptionsForBundle = subscriptionsPerBundle.get(bundleId);
final String externalKey = subscriptionsForBundle.get(0).getExternalKey();
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone,
- accountId,
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountId,
bundleId,
externalKey,
- accountEntitlements.getEntitlements().get(bundleId));
+ accountEntitlements.getEntitlements().get(bundleId),
+ internalTenantContextWithValidAccountRecordId);
final SubscriptionBaseBundle baseBundle = accountEntitlements.getBundles().get(bundleId);
final SubscriptionBundle subscriptionBundle = new DefaultSubscriptionBundle(bundleId,
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
index 91e5758..3d9f2ef 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -21,7 +21,7 @@ package org.killbill.billing.entitlement.api;
import java.util.List;
import java.util.UUID;
-import org.joda.time.DateTimeZone;
+import org.killbill.billing.callcontext.InternalTenantContext;
public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTimeline {
@@ -30,11 +30,15 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
private final String externalKey;
private final List<SubscriptionEvent> events;
- public DefaultSubscriptionBundleTimeline(final DateTimeZone accountTimeZone, final UUID accountId, final UUID bundleId, final String externalKey, final Iterable<Entitlement> entitlements) {
+ public DefaultSubscriptionBundleTimeline(final UUID accountId,
+ final UUID bundleId,
+ final String externalKey,
+ final Iterable<Entitlement> entitlements,
+ final InternalTenantContext internalTenantContext) {
this.accountId = accountId;
this.bundleId = bundleId;
this.externalKey = externalKey;
- this.events = SubscriptionEventOrdering.sortedCopy(entitlements, accountTimeZone);
+ this.events = SubscriptionEventOrdering.sortedCopy(entitlements, internalTenantContext);
}
@Override
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionEvent.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionEvent.java
index 9035efc..ea983ad 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionEvent.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionEvent.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -19,9 +21,8 @@ package org.killbill.billing.entitlement.api;
import java.util.UUID;
import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
-
+import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Plan;
import org.killbill.billing.catalog.api.PlanPhase;
@@ -50,7 +51,7 @@ public class DefaultSubscriptionEvent implements SubscriptionEvent {
private final PriceList nextPriceList;
private final BillingPeriod nextBillingPeriod;
private final DateTime createdDate;
- private final DateTimeZone accountTimeZone;
+ private final InternalTenantContext internalTenantContext;
public DefaultSubscriptionEvent(final UUID id,
final UUID entitlementId,
@@ -71,7 +72,7 @@ public class DefaultSubscriptionEvent implements SubscriptionEvent {
final PriceList nextPriceList,
final BillingPeriod nextBillingPeriod,
final DateTime createDate,
- final DateTimeZone accountTimeZone) {
+ final InternalTenantContext internalTenantContext) {
this.id = id;
this.entitlementId = entitlementId;
this.effectiveDate = effectiveDate;
@@ -92,11 +93,7 @@ public class DefaultSubscriptionEvent implements SubscriptionEvent {
this.nextPriceList = nextPriceList;
this.nextBillingPeriod = nextBillingPeriod;
this.createdDate = createDate;
- this.accountTimeZone = accountTimeZone;
- }
-
- public DateTimeZone getAccountTimeZone() {
- return accountTimeZone;
+ this.internalTenantContext = internalTenantContext;
}
public DateTime getEffectiveDateTime() {
@@ -119,12 +116,7 @@ public class DefaultSubscriptionEvent implements SubscriptionEvent {
@Override
public LocalDate getEffectiveDate() {
- return effectiveDate != null ? new LocalDate(effectiveDate, accountTimeZone) : null;
- }
-
- @Override
- public LocalDate getRequestedDate() {
- return requestedDate != null ? new LocalDate(requestedDate, accountTimeZone) : null;
+ return effectiveDate != null ? internalTenantContext.toLocalDate(effectiveDate) : null;
}
@Override
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/EntitlementDateHelper.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/EntitlementDateHelper.java
index d802daa..dbc48e8 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/EntitlementDateHelper.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/EntitlementDateHelper.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -16,47 +18,38 @@
package org.killbill.billing.entitlement.api;
+import javax.annotation.Nullable;
+
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
-import org.killbill.billing.account.api.AccountApiException;
-import org.killbill.billing.account.api.AccountInternalApi;
-import org.killbill.billing.account.api.ImmutableAccountData;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.clock.Clock;
-import org.killbill.clock.ClockUtil;
public class EntitlementDateHelper {
- private final AccountInternalApi accountApi;
private final Clock clock;
- public EntitlementDateHelper(final AccountInternalApi accountApi, final Clock clock) {
- this.accountApi = accountApi;
+ public EntitlementDateHelper(final Clock clock) {
this.clock = clock;
}
- public DateTime fromLocalDateAndReferenceTime(final LocalDate requestedDate, final DateTime referenceDateTime, final InternalTenantContext callContext) throws EntitlementApiException {
- try {
- final ImmutableAccountData account = accountApi.getImmutableAccountDataByRecordId(callContext.getAccountRecordId(), callContext);
- return ClockUtil.computeDateTimeWithUTCReferenceTime(requestedDate, referenceDateTime.toDateTime(DateTimeZone.UTC).toLocalTime(), account.getTimeZone(), clock);
- } catch (AccountApiException e) {
- throw new EntitlementApiException(e);
- }
+ public DateTime fromLocalDateAndReferenceTime(@Nullable final LocalDate requestedDate, final InternalTenantContext callContext) throws EntitlementApiException {
+ return requestedDate == null ? clock.getUTCNow() : callContext.toUTCDateTime(requestedDate);
}
/**
* Check if the date portion of a date/time is before or equals at now (as returned by the clock).
*
- * @param inputDate the fully qualified DateTime
- * @param accountTimeZone the account timezone
+ * @param inputDate the fully qualified DateTime
+ * @param accountTimeZone the account timezone
+ * @param internalTenantContext the context
* @return true if the inputDate, once converted into a LocalDate using account timezone is less or equals than today
*/
// TODO Move to ClockUtils
- public boolean isBeforeOrEqualsToday(final DateTime inputDate, final DateTimeZone accountTimeZone) {
- final LocalDate localDateNowInAccountTimezone = new LocalDate(clock.getUTCNow(), accountTimeZone);
- final LocalDate targetDateInAccountTimezone = new LocalDate(inputDate, accountTimeZone);
+ public boolean isBeforeOrEqualsToday(final DateTime inputDate, final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext) {
+ final LocalDate localDateNowInAccountTimezone = clock.getToday(accountTimeZone);
+ final LocalDate targetDateInAccountTimezone = internalTenantContext.toLocalDate(inputDate);
return targetDateInAccountTimezone.compareTo(localDateNowInAccountTimezone) <= 0;
}
-
}
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java
index 26ae84e..9283363 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -24,7 +24,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import org.joda.time.DateTimeZone;
+import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.entitlement.DefaultEntitlementService;
import org.killbill.billing.subscription.api.SubscriptionBase;
import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
@@ -47,20 +47,20 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
private SubscriptionEventOrdering() {}
- public static List<SubscriptionEvent> sortedCopy(final Entitlement entitlement, final DateTimeZone accountTimeZone) {
- return sortedCopy(ImmutableList.<Entitlement>of(entitlement), accountTimeZone);
+ public static List<SubscriptionEvent> sortedCopy(final Entitlement entitlement, final InternalTenantContext internalTenantContext) {
+ return sortedCopy(ImmutableList.<Entitlement>of(entitlement), internalTenantContext);
}
- public static List<SubscriptionEvent> sortedCopy(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone) {
- return INSTANCE.computeEvents(entitlements, accountTimeZone);
+ public static List<SubscriptionEvent> sortedCopy(final Iterable<Entitlement> entitlements, final InternalTenantContext internalTenantContext) {
+ return INSTANCE.computeEvents(entitlements, internalTenantContext);
}
- private List<SubscriptionEvent> computeEvents(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone) {
+ private List<SubscriptionEvent> computeEvents(final Iterable<Entitlement> entitlements, final InternalTenantContext internalTenantContext) {
// Compute base events across all entitlements (already ordered per entitlement)
- final LinkedList<SubscriptionEvent> result = computeSubscriptionBaseEvents(entitlements, accountTimeZone);
+ final LinkedList<SubscriptionEvent> result = computeSubscriptionBaseEvents(entitlements, internalTenantContext);
// Add blocking states at the right place
- BlockingStateOrdering.insertSorted(entitlements, accountTimeZone, result);
+ BlockingStateOrdering.insertSorted(entitlements, internalTenantContext, result);
// Final cleanups
reOrderSubscriptionEventsOnSameDateByType(result);
@@ -70,7 +70,7 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
}
// Compute the initial stream of events based on the subscription base events
- private LinkedList<SubscriptionEvent> computeSubscriptionBaseEvents(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone) {
+ private LinkedList<SubscriptionEvent> computeSubscriptionBaseEvents(final Iterable<Entitlement> entitlements, final InternalTenantContext internalTenantContext) {
final LinkedList<SubscriptionEvent> result = new LinkedList<SubscriptionEvent>();
for (final Entitlement cur : entitlements) {
Preconditions.checkState(cur instanceof DefaultEntitlement, "Entitlement %s is not a DefaultEntitlement", cur);
@@ -79,7 +79,7 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
for (final SubscriptionBaseTransition tr : baseTransitions) {
final List<SubscriptionEventType> eventTypes = toEventTypes(tr.getTransitionType());
for (final SubscriptionEventType eventType : eventTypes) {
- final SubscriptionEvent event = toSubscriptionEvent(tr, eventType, accountTimeZone);
+ final SubscriptionEvent event = toSubscriptionEvent(tr, eventType, internalTenantContext);
insertSubscriptionEvent(event, result);
}
}
@@ -91,12 +91,8 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
private List<SubscriptionEventType> toEventTypes(final SubscriptionBaseTransitionType in) {
switch (in) {
case CREATE:
- return ImmutableList.<SubscriptionEventType>of(SubscriptionEventType.START_ENTITLEMENT, SubscriptionEventType.START_BILLING);
+ return ImmutableList.<SubscriptionEventType>of(SubscriptionEventType.START_BILLING);
case TRANSFER:
- return ImmutableList.<SubscriptionEventType>of(SubscriptionEventType.START_ENTITLEMENT, SubscriptionEventType.START_BILLING);
- case MIGRATE_ENTITLEMENT:
- return ImmutableList.<SubscriptionEventType>of(SubscriptionEventType.START_ENTITLEMENT);
- case MIGRATE_BILLING:
return ImmutableList.<SubscriptionEventType>of(SubscriptionEventType.START_BILLING);
case CHANGE:
return ImmutableList.<SubscriptionEventType>of(SubscriptionEventType.CHANGE);
@@ -150,7 +146,7 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
result.add(index, event);
}
- private SubscriptionEvent toSubscriptionEvent(final SubscriptionBaseTransition in, final SubscriptionEventType eventType, final DateTimeZone accountTimeZone) {
+ private SubscriptionEvent toSubscriptionEvent(final SubscriptionBaseTransition in, final SubscriptionEventType eventType, final InternalTenantContext internalTenantContext) {
return new DefaultSubscriptionEvent(in.getId(),
in.getSubscriptionId(),
in.getEffectiveTransitionTime(),
@@ -170,7 +166,7 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
in.getNextPriceList(),
(in.getNextPlan() != null ? in.getNextPlan().getRecurringBillingPeriod() : null),
in.getCreatedDate(),
- accountTimeZone);
+ internalTenantContext);
}
//
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
index 59f2c89..b1a720c 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -38,11 +38,9 @@ import org.killbill.billing.entitlement.AccountEntitlements;
import org.killbill.billing.entitlement.AccountEventsStreams;
import org.killbill.billing.entitlement.DefaultEntitlementService;
import org.killbill.billing.entitlement.EntitlementService;
-import org.killbill.billing.entitlement.EntitlementTransitionType;
import org.killbill.billing.entitlement.EventsStream;
import org.killbill.billing.entitlement.api.BlockingState;
import org.killbill.billing.entitlement.api.BlockingStateType;
-import org.killbill.billing.entitlement.api.DefaultEffectiveEntitlementEvent;
import org.killbill.billing.entitlement.api.DefaultEntitlement;
import org.killbill.billing.entitlement.api.DefaultEntitlementApi;
import org.killbill.billing.entitlement.api.DefaultEntitlementContext;
@@ -69,7 +67,6 @@ import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.bus.api.PersistentBus;
-import org.killbill.bus.api.PersistentBus.EventBusException;
import org.killbill.clock.Clock;
import org.killbill.notificationq.api.NotificationEvent;
import org.killbill.notificationq.api.NotificationQueue;
@@ -120,11 +117,10 @@ public class DefaultEntitlementApiBase {
this.eventsStreamBuilder = eventsStreamBuilder;
this.entitlementUtils = entitlementUtils;
this.securityApi = securityApi;
- this.dateHelper = new EntitlementDateHelper(accountApi, clock);
+ this.dateHelper = new EntitlementDateHelper(clock);
}
- public AccountEntitlements getAllEntitlementsForAccountId(final UUID accountId, final InternalTenantContext tenantContext) throws EntitlementApiException {
-
+ public AccountEntitlements getAllEntitlementsForAccount(final InternalTenantContext tenantContext) throws EntitlementApiException {
final AccountEventsStreams accountEventsStreams = eventsStreamBuilder.buildForAccount(tenantContext);
final Map<UUID, Collection<Entitlement>> entitlementsPerBundle = new HashMap<UUID, Collection<Entitlement>>();
@@ -136,7 +132,7 @@ public class DefaultEntitlementApiBase {
for (final EventsStream eventsStream : accountEventsStreams.getEventsStreams().get(bundleId)) {
final Entitlement entitlement = new DefaultEntitlement(eventsStream, eventsStreamBuilder, entitlementApi, pluginExecution,
blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
- entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory);
+ entitlementUtils, dateHelper, clock, securityApi, tenantContext, internalCallContextFactory);
entitlementsPerBundle.get(bundleId).add(entitlement);
}
}
@@ -148,18 +144,20 @@ public class DefaultEntitlementApiBase {
final EventsStream eventsStream = eventsStreamBuilder.buildForEntitlement(entitlementId, tenantContext);
return new DefaultEntitlement(eventsStream, eventsStreamBuilder, entitlementApi, pluginExecution,
blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
- entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory);
+ entitlementUtils, dateHelper, clock, securityApi, tenantContext, internalCallContextFactory);
}
- public void pause(final UUID bundleId, final LocalDate localEffectiveDate, final Iterable<PluginProperty> properties, final InternalCallContext internalCallContext) throws EntitlementApiException {
+ public void pause(final UUID bundleId, @Nullable final LocalDate localEffectiveDate, final Iterable<PluginProperty> properties, final InternalCallContext internalCallContext) throws EntitlementApiException {
- final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.PAUSE_SUBSCRIPTION,
+ final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.PAUSE_BUNDLE,
null,
null,
bundleId,
null,
null,
localEffectiveDate,
+ localEffectiveDate,
+ null,
properties,
internalCallContextFactory.createCallContext(internalCallContext));
@@ -171,27 +169,14 @@ public class DefaultEntitlementApiBase {
final SubscriptionBaseBundle bundle = subscriptionInternalApi.getBundleFromId(bundleId, internalCallContext);
final ImmutableAccountData account = accountApi.getImmutableAccountDataById(bundle.getAccountId(), internalCallContext);
final SubscriptionBase baseSubscription = subscriptionInternalApi.getBaseSubscription(bundleId, internalCallContext);
- final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), baseSubscription.getStartDate(), internalCallContext);
+ final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getBillingEffectiveDate(), internalCallContext);
- if (!dateHelper.isBeforeOrEqualsToday(effectiveDate, account.getTimeZone())) {
+ if (!dateHelper.isBeforeOrEqualsToday(effectiveDate, account.getTimeZone(), internalCallContext)) {
recordPauseResumeNotificationEntry(baseSubscription.getId(), bundleId, effectiveDate, true, internalCallContext);
return null;
}
- final UUID blockingId = blockUnblockBundle(bundleId, DefaultEntitlementApi.ENT_STATE_BLOCKED, EntitlementService.ENTITLEMENT_SERVICE_NAME, localEffectiveDate, true, true, true, baseSubscription, internalCallContext);
-
- // Should we send one event per entitlement in the bundle?
- // Code below only sends one event for the bundle and use the base entitlementId
- final DefaultEffectiveEntitlementEvent event = new DefaultEffectiveEntitlementEvent(blockingId, baseSubscription.getId(), bundleId, bundle.getAccountId(), EntitlementTransitionType.BLOCK_BUNDLE,
- effectiveDate, clock.getUTCNow(),
- internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId(),
- internalCallContext.getUserToken());
-
- try {
- eventBus.post(event);
- } catch (EventBusException e) {
- log.warn("Failed to post bus event for pause operation on bundle " + bundleId);
- }
+ blockUnblockBundle(bundleId, DefaultEntitlementApi.ENT_STATE_BLOCKED, EntitlementService.ENTITLEMENT_SERVICE_NAME, localEffectiveDate, true, true, true, baseSubscription, internalCallContext);
} catch (SubscriptionBaseApiException e) {
throw new EntitlementApiException(e);
@@ -204,15 +189,17 @@ public class DefaultEntitlementApiBase {
pluginExecution.executeWithPlugin(pauseWithPlugin, pluginContext);
}
- public void resume(final UUID bundleId, final LocalDate localEffectiveDate, final Iterable<PluginProperty> properties, final InternalCallContext internalCallContext) throws EntitlementApiException {
+ public void resume(final UUID bundleId, @Nullable final LocalDate localEffectiveDate, final Iterable<PluginProperty> properties, final InternalCallContext internalCallContext) throws EntitlementApiException {
- final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.RESUME_SUBSCRIPTION,
+ final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.RESUME_BUNDLE,
null,
null,
bundleId,
null,
null,
localEffectiveDate,
+ localEffectiveDate,
+ null,
properties,
internalCallContextFactory.createCallContext(internalCallContext));
final WithEntitlementPlugin<Void> resumeWithPlugin = new WithEntitlementPlugin<Void>() {
@@ -223,27 +210,14 @@ public class DefaultEntitlementApiBase {
final ImmutableAccountData account = accountApi.getImmutableAccountDataById(bundle.getAccountId(), internalCallContext);
final SubscriptionBase baseSubscription = subscriptionInternalApi.getBaseSubscription(bundleId, internalCallContext);
- final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), baseSubscription.getStartDate(), internalCallContext);
+ final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getBillingEffectiveDate(), internalCallContext);
- if (!dateHelper.isBeforeOrEqualsToday(effectiveDate, account.getTimeZone())) {
+ if (!dateHelper.isBeforeOrEqualsToday(effectiveDate, account.getTimeZone(), internalCallContext)) {
recordPauseResumeNotificationEntry(baseSubscription.getId(), bundleId, effectiveDate, false, internalCallContext);
return null;
}
- final UUID blockingId = blockUnblockBundle(bundleId, DefaultEntitlementApi.ENT_STATE_CLEAR, EntitlementService.ENTITLEMENT_SERVICE_NAME, localEffectiveDate, false, false, false, baseSubscription, internalCallContext);
-
- // Should we send one event per entitlement in the bundle?
- // Code below only sends one event for the bundle and use the base entitlementId
- final DefaultEffectiveEntitlementEvent event = new DefaultEffectiveEntitlementEvent(blockingId, baseSubscription.getId(), bundleId, bundle.getAccountId(), EntitlementTransitionType.UNBLOCK_BUNDLE,
- effectiveDate, clock.getUTCNow(),
- internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId(),
- internalCallContext.getUserToken());
-
- try {
- eventBus.post(event);
- } catch (EventBusException e) {
- log.warn("Failed to post bus event for resume operation on bundle " + bundleId);
- }
+ blockUnblockBundle(bundleId, DefaultEntitlementApi.ENT_STATE_CLEAR, EntitlementService.ENTITLEMENT_SERVICE_NAME, localEffectiveDate, false, false, false, baseSubscription, internalCallContext);
} catch (SubscriptionBaseApiException e) {
throw new EntitlementApiException(e);
@@ -261,17 +235,12 @@ public class DefaultEntitlementApiBase {
blockUnblockBundle(bundleId, stateName, serviceName, localEffectiveDate, blockBilling, blockEntitlement, blockChange, null, internalCallContext);
}
- private UUID blockUnblockBundle(final UUID bundleId, final String stateName, final String serviceName, final LocalDate localEffectiveDate, boolean blockBilling, boolean blockEntitlement, boolean blockChange, @Nullable final SubscriptionBase inputBaseSubscription, final InternalCallContext internalCallContext)
+ private UUID blockUnblockBundle(final UUID bundleId, final String stateName, final String serviceName, @Nullable final LocalDate localEffectiveDate, boolean blockBilling, boolean blockEntitlement, boolean blockChange, @Nullable final SubscriptionBase inputBaseSubscription, final InternalCallContext internalCallContext)
throws EntitlementApiException {
- try {
- final SubscriptionBase baseSubscription = inputBaseSubscription == null ? subscriptionInternalApi.getBaseSubscription(bundleId, internalCallContext) : inputBaseSubscription;
- final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(localEffectiveDate, baseSubscription.getStartDate(), internalCallContext);
- final BlockingState state = new DefaultBlockingState(bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE, stateName, serviceName, blockChange, blockEntitlement, blockBilling, effectiveDate);
- entitlementUtils.setBlockingStatesAndPostBlockingTransitionEvent(ImmutableList.<BlockingState>of(state), bundleId, internalCallContext);
- return state.getId();
- } catch (final SubscriptionBaseApiException e) {
- throw new EntitlementApiException(e);
- }
+ final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(localEffectiveDate, internalCallContext);
+ final BlockingState state = new DefaultBlockingState(bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE, stateName, serviceName, blockChange, blockEntitlement, blockBilling, effectiveDate);
+ entitlementUtils.setBlockingStatesAndPostBlockingTransitionEvent(ImmutableList.<BlockingState>of(state), bundleId, internalCallContext);
+ return state.getId();
}
protected void recordPauseResumeNotificationEntry(final UUID entitlementId, final UUID bundleId, final DateTime effectiveDate, final boolean isPause, final InternalCallContext contextWithValidAccountRecordId) throws EntitlementApiException {
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
index d77e4ab..3fc96da 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
@@ -28,6 +28,7 @@ import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable;
+import javax.annotation.Nullable;
import javax.inject.Inject;
import org.joda.time.DateTime;
@@ -91,7 +92,7 @@ public class DefaultEntitlementInternalApi extends DefaultEntitlementApiBase imp
}
@Override
- public void cancel(final Iterable<Entitlement> entitlements, final LocalDate effectiveDate, final BillingActionPolicy billingPolicy, final Iterable<PluginProperty> properties, final InternalCallContext internalCallContext) throws EntitlementApiException {
+ public void cancel(final Iterable<Entitlement> entitlements, @Nullable final LocalDate effectiveDate, final BillingActionPolicy billingPolicy, final Iterable<PluginProperty> properties, final InternalCallContext internalCallContext) throws EntitlementApiException {
final CallContext callContext = internalCallContextFactory.createCallContext(internalCallContext);
final ImmutableMap.Builder<BlockingState, Optional<UUID>> blockingStates = new ImmutableMap.Builder<BlockingState, Optional<UUID>>();
@@ -113,6 +114,8 @@ public class DefaultEntitlementInternalApi extends DefaultEntitlementApiBase imp
entitlement.getExternalKey(),
null,
effectiveDate,
+ null,
+ billingPolicy,
properties,
callContext);
pluginContexts.add(pluginContext);
@@ -216,8 +219,7 @@ public class DefaultEntitlementInternalApi extends DefaultEntitlementApiBase imp
@Override
public Entitlement doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
- final LocalDate effectiveLocalDate = new LocalDate(updatedPluginContext.getEffectiveDate(), entitlement.getAccountTimeZone());
- DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(effectiveLocalDate, entitlement.getSubscriptionBase().getStartDate(), internalCallContext);
+ DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEntitlementEffectiveDate(), internalCallContext);
// Avoid timing issues for IMM cancellations (we don't want an entitlement cancel date one second or so after the subscription cancel date or
// add-ons cancellations computations won't work).
if (effectiveDate.compareTo(entitlement.getSubscriptionBase().getEndDate()) > 0) {
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java
index 9d67470..056bd3a 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java
@@ -47,6 +47,7 @@ import org.killbill.billing.entitlement.block.StatelessBlockingChecker;
import org.killbill.billing.entitlement.engine.core.BlockingTransitionNotificationKey;
import org.killbill.billing.util.cache.Cachable.CacheType;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.entity.dao.EntityDaoBase;
import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
@@ -112,8 +113,8 @@ public class DefaultBlockingStateDao extends EntityDaoBase<BlockingStateModelDao
private final StatelessBlockingChecker statelessBlockingChecker = new StatelessBlockingChecker();
public DefaultBlockingStateDao(final IDBI dbi, final Clock clock, final NotificationQueueService notificationQueueService, final PersistentBus eventBus,
- final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao) {
- super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao), BlockingStateSqlDao.class);
+ final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory) {
+ super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory), BlockingStateSqlDao.class);
this.clock = clock;
this.notificationQueueService = notificationQueueService;
this.eventBus = eventBus;
@@ -226,9 +227,11 @@ public class DefaultBlockingStateDao extends EntityDaoBase<BlockingStateModelDao
}
}
+ boolean inserted = false;
// Create the state, if needed
if (!blockingStatesToRemove.contains(newBlockingStateModelDao.getId())) {
sqlDao.create(newBlockingStateModelDao, context);
+ inserted = true;
}
final BlockingAggregator currentState = getBlockedStatus(sqlDao, entitySqlDaoWrapperFactory.getHandle(), state.getBlockedId(), state.getType(), bundleId, upToDate, context);
@@ -238,7 +241,9 @@ public class DefaultBlockingStateDao extends EntityDaoBase<BlockingStateModelDao
state.getEffectiveDate(),
state.getBlockedId(),
state.getType(),
+ state.getStateName(),
state.getService(),
+ inserted,
previousState,
currentState,
context);
@@ -277,7 +282,9 @@ public class DefaultBlockingStateDao extends EntityDaoBase<BlockingStateModelDao
final DateTime effectiveDate,
final UUID blockableId,
final BlockingStateType type,
+ final String stateName,
final String serviceName,
+ final boolean blockingStateInserted,
final BlockingAggregator previousState,
final BlockingAggregator currentState,
final InternalCallContext context) {
@@ -289,18 +296,31 @@ public class DefaultBlockingStateDao extends EntityDaoBase<BlockingStateModelDao
if (effectiveDate.compareTo(clock.getUTCNow()) > 0) {
// Add notification entry to send the bus event at the effective date
- final NotificationEvent notificationEvent = new BlockingTransitionNotificationKey(blockingStateId, blockableId, type,
- isTransitionToBlockedBilling, isTransitionToUnblockedBilling,
- isTransitionToBlockedEntitlement, isTransitionToUnblockedEntitlement);
+ final NotificationEvent notificationEvent = new BlockingTransitionNotificationKey(blockingStateId,
+ blockableId,
+ stateName,
+ serviceName,
+ effectiveDate,
+ type,
+ isTransitionToBlockedBilling,
+ isTransitionToUnblockedBilling,
+ isTransitionToBlockedEntitlement,
+ isTransitionToUnblockedEntitlement);
recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory, effectiveDate, notificationEvent, context);
} else {
- // TODO Do we want to send a DefaultEffectiveEntitlementEvent for entitlement specific blocking states?
- // Don't post if nothing has changed for entitlement-service
- if (!serviceName.equals(EntitlementService.ENTITLEMENT_SERVICE_NAME) || !previousState.equals(currentState)) {
- final BusEvent event = new DefaultBlockingTransitionInternalEvent(blockableId, type,
- isTransitionToBlockedBilling, isTransitionToUnblockedBilling,
- isTransitionToBlockedEntitlement, isTransitionToUnblockedEntitlement,
- context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
+ if (blockingStateInserted) {
+ final BusEvent event = new DefaultBlockingTransitionInternalEvent(blockableId,
+ stateName,
+ serviceName,
+ effectiveDate,
+ type,
+ isTransitionToBlockedBilling,
+ isTransitionToUnblockedBilling,
+ isTransitionToBlockedEntitlement,
+ isTransitionToUnblockedEntitlement,
+ context.getAccountRecordId(),
+ context.getTenantRecordId(),
+ context.getUserToken());
notifyBusFromTransaction(entitySqlDaoWrapperFactory, event);
} else {
log.debug("Skipping event for service {} and blockableId {} (previousState={}, currentState={})", serviceName, blockableId, previousState, currentState);
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/OptimizedProxyBlockingStateDao.java b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/OptimizedProxyBlockingStateDao.java
index 3c228f2..35c718a 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/OptimizedProxyBlockingStateDao.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/OptimizedProxyBlockingStateDao.java
@@ -35,6 +35,7 @@ import org.killbill.billing.subscription.api.SubscriptionBase;
import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.bus.api.PersistentBus;
import org.killbill.clock.Clock;
@@ -47,8 +48,8 @@ public class OptimizedProxyBlockingStateDao extends ProxyBlockingStateDao {
public OptimizedProxyBlockingStateDao(final EventsStreamBuilder eventsStreamBuilder, final SubscriptionBaseInternalApi subscriptionBaseInternalApi,
final IDBI dbi, final Clock clock, final NotificationQueueService notificationQueueService, final PersistentBus eventBus,
- final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao) {
- super(eventsStreamBuilder, subscriptionBaseInternalApi, dbi, clock, notificationQueueService, eventBus, cacheControllerDispatcher, nonEntityDao);
+ final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory) {
+ super(eventsStreamBuilder, subscriptionBaseInternalApi, dbi, clock, notificationQueueService, eventBus, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory);
}
/**
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/ProxyBlockingStateDao.java b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/ProxyBlockingStateDao.java
index 1defc97..ea237a3 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/ProxyBlockingStateDao.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/ProxyBlockingStateDao.java
@@ -44,6 +44,7 @@ import org.killbill.billing.subscription.api.SubscriptionBase;
import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.customfield.ShouldntHappenException;
import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.entity.Pagination;
@@ -171,11 +172,11 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
@Inject
public ProxyBlockingStateDao(final EventsStreamBuilder eventsStreamBuilder, final SubscriptionBaseInternalApi subscriptionBaseInternalApi,
final IDBI dbi, final Clock clock, final NotificationQueueService notificationQueueService, final PersistentBus eventBus,
- final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao) {
+ final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory) {
this.eventsStreamBuilder = eventsStreamBuilder;
this.subscriptionInternalApi = subscriptionBaseInternalApi;
this.clock = clock;
- this.delegate = new DefaultBlockingStateDao(dbi, clock, notificationQueueService, eventBus, cacheControllerDispatcher, nonEntityDao);
+ this.delegate = new DefaultBlockingStateDao(dbi, clock, notificationQueueService, eventBus, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory);
}
@Override
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java b/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java
index 7e189c0..68465ae 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java
@@ -142,9 +142,9 @@ public class DefaultEntitlementService implements EntitlementService {
EntitlementNotificationKeyAction.CANCEL.equals(entitlementNotificationKeyAction)) {
blockAddOnsIfRequired(key, (DefaultEntitlement) entitlement, callContext, internalCallContext);
} else if (EntitlementNotificationKeyAction.PAUSE.equals(entitlementNotificationKeyAction)) {
- entitlementInternalApi.pause(key.getBundleId(), key.getEffectiveDate().toLocalDate(), ImmutableList.<PluginProperty>of(), internalCallContext);
+ entitlementInternalApi.pause(key.getBundleId(), internalCallContext.toLocalDate(key.getEffectiveDate()), ImmutableList.<PluginProperty>of(), internalCallContext);
} else if (EntitlementNotificationKeyAction.RESUME.equals(entitlementNotificationKeyAction)) {
- entitlementInternalApi.resume(key.getBundleId(), key.getEffectiveDate().toLocalDate(), ImmutableList.<PluginProperty>of(), internalCallContext);
+ entitlementInternalApi.resume(key.getBundleId(), internalCallContext.toLocalDate(key.getEffectiveDate()), ImmutableList.<PluginProperty>of(), internalCallContext);
}
} catch (final EntitlementApiException e) {
log.error("Error processing event for entitlement {}" + entitlement.getId(), e);
@@ -182,10 +182,18 @@ public class DefaultEntitlementService implements EntitlementService {
return;
}
- final BusEvent event = new DefaultBlockingTransitionInternalEvent(key.getBlockableId(), key.getBlockingType(),
- key.isTransitionedToBlockedBilling(), key.isTransitionedToUnblockedBilling(),
- key.isTransitionedToBlockedEntitlement(), key.isTransitionToUnblockedEntitlement(),
- internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId(), internalCallContext.getUserToken());
+ final BusEvent event = new DefaultBlockingTransitionInternalEvent(key.getBlockableId(),
+ key.getStateName(),
+ key.getService(),
+ key.getEffectiveDate(),
+ key.getBlockingType(),
+ key.isTransitionedToBlockedBilling(),
+ key.isTransitionedToUnblockedBilling(),
+ key.isTransitionedToBlockedEntitlement(),
+ key.isTransitionToUnblockedEntitlement(),
+ internalCallContext.getAccountRecordId(),
+ internalCallContext.getTenantRecordId(),
+ internalCallContext.getUserToken());
try {
eventBus.post(event);
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/BlockingTransitionNotificationKey.java b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/BlockingTransitionNotificationKey.java
index fc4b02d..93035ad 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/BlockingTransitionNotificationKey.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/BlockingTransitionNotificationKey.java
@@ -18,6 +18,7 @@ package org.killbill.billing.entitlement.engine.core;
import java.util.UUID;
+import org.joda.time.DateTime;
import org.killbill.billing.entitlement.api.BlockingStateType;
import org.killbill.notificationq.api.NotificationEvent;
@@ -29,6 +30,9 @@ public class BlockingTransitionNotificationKey implements NotificationEvent {
private final UUID blockingStateId;
private final UUID blockableId;
private final BlockingStateType blockingType;
+ private final String stateName;
+ private final String service;
+ private final DateTime effectiveDate;
private final Boolean isTransitionToBlockedBilling;
private final Boolean isTransitionToUnblockedBilling;
private final Boolean isTransitionToBlockedEntitlement;
@@ -37,6 +41,9 @@ public class BlockingTransitionNotificationKey implements NotificationEvent {
@JsonCreator
public BlockingTransitionNotificationKey(@JsonProperty("blockingStateId") final UUID blockingStateId,
@JsonProperty("blockableId") final UUID blockableId,
+ @JsonProperty("stateName") final String stateName,
+ @JsonProperty("service") final String service,
+ @JsonProperty("effectiveDate") final DateTime effectiveDate,
@JsonProperty("type") final BlockingStateType blockingType,
@JsonProperty("isTransitionToBlockedBilling") final Boolean isTransitionToBlockedBilling,
@JsonProperty("isTransitionToUnblockedBilling") final Boolean isTransitionToUnblockedBilling,
@@ -45,6 +52,9 @@ public class BlockingTransitionNotificationKey implements NotificationEvent {
this.blockingStateId = blockingStateId;
this.blockableId = blockableId;
+ this.service = service;
+ this.stateName = stateName;
+ this.effectiveDate = effectiveDate;
this.blockingType = blockingType;
this.isTransitionToBlockedBilling = isTransitionToBlockedBilling;
this.isTransitionToUnblockedBilling = isTransitionToUnblockedBilling;
@@ -64,6 +74,18 @@ public class BlockingTransitionNotificationKey implements NotificationEvent {
return blockingType;
}
+ public String getStateName() {
+ return stateName;
+ }
+
+ public String getService() {
+ return service;
+ }
+
+ public DateTime getEffectiveDate() {
+ return effectiveDate;
+ }
+
@JsonProperty("isTransitionToBlockedBilling")
public Boolean isTransitionedToBlockedBilling() {
return isTransitionToBlockedBilling;
@@ -115,6 +137,15 @@ public class BlockingTransitionNotificationKey implements NotificationEvent {
if (blockableId != null ? !blockableId.equals(that.blockableId) : that.blockableId != null) {
return false;
}
+ if (stateName != null ? !stateName.equals(that.stateName) : that.stateName != null) {
+ return false;
+ }
+ if (service != null ? !service.equals(that.service) : that.service != null) {
+ return false;
+ }
+ if (effectiveDate != null ? effectiveDate.compareTo(that.effectiveDate) != 0 : that.effectiveDate != null) {
+ return false;
+ }
if (blockingType != that.blockingType) {
return false;
}
@@ -138,6 +169,9 @@ public class BlockingTransitionNotificationKey implements NotificationEvent {
public int hashCode() {
int result = blockingStateId != null ? blockingStateId.hashCode() : 0;
result = 31 * result + (blockableId != null ? blockableId.hashCode() : 0);
+ result = 31 * result + (stateName != null ? stateName.hashCode() : 0);
+ result = 31 * result + (service != null ? service.hashCode() : 0);
+ result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
result = 31 * result + (blockingType != null ? blockingType.hashCode() : 0);
result = 31 * result + (isTransitionToBlockedBilling != null ? isTransitionToBlockedBilling.hashCode() : 0);
result = 31 * result + (isTransitionToUnblockedBilling != null ? isTransitionToUnblockedBilling.hashCode() : 0);
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
index e6d701e..d7e4799 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -71,7 +73,9 @@ public class DefaultEventsStream implements EventsStream {
private BlockingAggregator blockingAggregator;
private List<BlockingState> subscriptionEntitlementStates;
+ private LocalDate entitlementEffectiveStartDate;
private LocalDate entitlementEffectiveEndDate;
+ private BlockingState entitlementStartEvent;
private BlockingState entitlementCancelEvent;
private EntitlementState entitlementState;
@@ -143,6 +147,11 @@ public class DefaultEventsStream implements EventsStream {
}
@Override
+ public LocalDate getEntitlementEffectiveStartDate() {
+ return entitlementEffectiveStartDate;
+ }
+
+ @Override
public boolean isBlockChange() {
return blockingAggregator.isBlockChange();
}
@@ -353,6 +362,7 @@ public class DefaultEventsStream implements EventsStream {
private void setup() {
computeEntitlementBlockingStates();
computeBlockingAggregator();
+ computeEntitlementStartEvent();
computeEntitlementCancelEvent();
computeStateForEntitlement();
}
@@ -390,6 +400,20 @@ public class DefaultEventsStream implements EventsStream {
return ImmutableList.<BlockingState>copyOf(currentBlockingStatePerService.values());
}
+ private void computeEntitlementStartEvent() {
+ entitlementStartEvent = Iterables.<BlockingState>tryFind(subscriptionEntitlementStates,
+ new Predicate<BlockingState>() {
+ @Override
+ public boolean apply(final BlockingState input) {
+ return DefaultEntitlementApi.ENT_STATE_START.equals(input.getStateName());
+ }
+ }).orNull();
+
+ // Note that we still default to subscriptionBase.startDate (for compatibility issue where ENT_STATE_START does not exist)
+ entitlementEffectiveStartDate = entitlementStartEvent != null ?
+ internalTenantContext.toLocalDate(entitlementStartEvent.getEffectiveDate()) :
+ internalTenantContext.toLocalDate(getSubscriptionBase().getStartDate());
+ }
private void computeEntitlementCancelEvent() {
entitlementCancelEvent = Iterables.<BlockingState>tryFind(subscriptionEntitlementStates,
@@ -399,16 +423,20 @@ public class DefaultEventsStream implements EventsStream {
return DefaultEntitlementApi.ENT_STATE_CANCELLED.equals(input.getStateName());
}
}).orNull();
- entitlementEffectiveEndDate = entitlementCancelEvent != null ? new LocalDate(entitlementCancelEvent.getEffectiveDate(), account.getTimeZone()) : null;
+ entitlementEffectiveEndDate = entitlementCancelEvent != null ? internalTenantContext.toLocalDate(entitlementCancelEvent.getEffectiveDate()) : null;
}
private void computeStateForEntitlement() {
// Current state for the ENTITLEMENT_SERVICE_NAME is set to cancelled
- if (entitlementEffectiveEndDate != null && entitlementEffectiveEndDate.compareTo(new LocalDate(utcNow, account.getTimeZone())) <= 0) {
+ if (entitlementEffectiveEndDate != null && entitlementEffectiveEndDate.compareTo(internalTenantContext.toLocalDate(utcNow)) <= 0) {
entitlementState = EntitlementState.CANCELLED;
} else {
- // Gather states across all services and check if one of them is set to 'blockEntitlement'
- entitlementState = (blockingAggregator != null && blockingAggregator.isBlockEntitlement() ? EntitlementState.BLOCKED : EntitlementState.ACTIVE);
+ if (entitlementEffectiveStartDate.compareTo(new LocalDate(utcNow, account.getTimeZone())) > 0) {
+ entitlementState = EntitlementState.PENDING;
+ } else {
+ // Gather states across all services and check if one of them is set to 'blockEntitlement'
+ entitlementState = (blockingAggregator != null && blockingAggregator.isBlockEntitlement() ? EntitlementState.BLOCKED : EntitlementState.ACTIVE);
+ }
}
}
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
index b090a03..dc6a097 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
@@ -89,8 +89,8 @@ public class EventsStreamBuilder {
this.clock = clock;
this.internalCallContextFactory = internalCallContextFactory;
- this.defaultBlockingStateDao = new DefaultBlockingStateDao(dbi, clock, notificationQueueService, eventBus, cacheControllerDispatcher, nonEntityDao);
- this.blockingStateDao = new OptimizedProxyBlockingStateDao(this, subscriptionInternalApi, dbi, clock, notificationQueueService, eventBus, cacheControllerDispatcher, nonEntityDao);
+ this.defaultBlockingStateDao = new DefaultBlockingStateDao(dbi, clock, notificationQueueService, eventBus, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory);
+ this.blockingStateDao = new OptimizedProxyBlockingStateDao(this, subscriptionInternalApi, dbi, clock, notificationQueueService, eventBus, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory);
}
public EventsStream refresh(final EventsStream eventsStream, final TenantContext tenantContext) throws EntitlementApiException {
@@ -100,7 +100,7 @@ public class EventsStreamBuilder {
public EventsStream buildForBaseSubscription(final UUID bundleId, final TenantContext tenantContext) throws EntitlementApiException {
final SubscriptionBase baseSubscription;
try {
- final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(tenantContext);
+ final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(bundleId, ObjectType.BUNDLE, tenantContext);
baseSubscription = subscriptionInternalApi.getBaseSubscription(bundleId, internalTenantContext);
} catch (SubscriptionBaseApiException e) {
throw new EntitlementApiException(e);
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlement.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlement.java
index e2a2bf1..7052c35 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlement.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlement.java
@@ -31,6 +31,7 @@ import org.killbill.billing.catalog.api.PriceListSet;
import org.killbill.billing.catalog.api.ProductCategory;
import org.killbill.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
import org.killbill.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementSourceType;
import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
@@ -48,26 +49,27 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
// Create entitlement and check each field
- testListener.pushExpectedEvent(NextEvent.CREATE);
- final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
+ assertEquals(entitlement.getSourceType(), EntitlementSourceType.NATIVE);
clock.addDays(5);
testListener.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK);
- final LocalDate cancelDate = new LocalDate(clock.getUTCNow());
- entitlement.cancelEntitlementWithDate(cancelDate, true, ImmutableList.<PluginProperty>of(), callContext);
+ entitlement.cancelEntitlementWithDate(null, true, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
final Entitlement entitlement2 = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
assertEquals(entitlement2.getState(), EntitlementState.CANCELLED);
- assertEquals(entitlement2.getEffectiveEndDate(), cancelDate);
+ assertEquals(entitlement2.getEffectiveEndDate(), clock.getUTCToday());
+ assertEquals(entitlement2.getSourceType(), EntitlementSourceType.NATIVE);
}
@Test(groups = "slow")
@@ -75,13 +77,13 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
// Create entitlement and check each field
- testListener.pushExpectedEvent(NextEvent.CREATE);
- final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
@@ -95,7 +97,6 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
assertEquals(entitlement2.getEffectiveEndDate(), cancelDate);
clock.addDays(1);
-
testListener.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK);
assertListenerStatus();
@@ -109,13 +110,13 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
// Create entitlement and check each field
- testListener.pushExpectedEvent(NextEvent.CREATE);
- final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
@@ -142,13 +143,13 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
// Create entitlement and check each field
- testListener.pushExpectedEvent(NextEvent.CREATE);
- final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
testListener.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK);
@@ -167,13 +168,13 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
// Create entitlement and check each field
- testListener.pushExpectedEvent(NextEvent.CREATE);
- final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
final DateTime ctd = clock.getUTCNow().plusDays(30).plusMonths(1);
@@ -210,13 +211,13 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
// Create entitlement and check each field
- testListener.pushExpectedEvent(NextEvent.CREATE);
- final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
final DateTime ctd = clock.getUTCNow().plusDays(30).plusMonths(1);
@@ -250,13 +251,13 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
// Create entitlement and check each field
- testListener.pushExpectedEvent(NextEvent.CREATE);
- final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
// Immediate change during trial
@@ -284,20 +285,20 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
// Create entitlement and check each field
- testListener.pushExpectedEvent(NextEvent.CREATE);
- final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
clock.addDays(1);
assertListenerStatus();
testListener.pushExpectedEvent(NextEvent.BLOCK);
- entitlementApi.setBlockingState(entitlement.getBundleId(), "MY_BLOCK", "test", clock.getUTCToday(), false, false, true, ImmutableList.<PluginProperty>of(), callContext);
+ entitlementApi.setBlockingState(entitlement.getBundleId(), "MY_BLOCK", "test", null, false, false, true, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
try {
@@ -327,4 +328,30 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
assertEquals(latestEntitlement.getLastActivePlan().getProduct().getName(), "Shotgun");
}
}
+
+ @Test(groups = "slow")
+ public void testEntitlementStartedInFuture() throws AccountApiException, EntitlementApiException {
+ final LocalDate initialDate = new LocalDate(2013, 8, 7);
+ clock.setDay(initialDate);
+
+
+ final LocalDate startDate = initialDate.plusDays(10);
+
+ final Account account = accountApi.createAccount(getAccountData(7), callContext);
+
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+ // Create entitlement and check each field
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, startDate, startDate, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+ assertEquals(entitlement.getState(), EntitlementState.PENDING);
+
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ clock.addDays(10);
+ assertListenerStatus();
+
+ final Entitlement entitlement1 = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
+ assertEquals(entitlement1.getState(), EntitlementState.ACTIVE);
+
+ }
}
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java
index 74fda0c..256616d 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -54,20 +56,20 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
// Keep the same object for the whole test, to make sure we refresh its state before r/w calls
- testListener.pushExpectedEvent(NextEvent.CREATE);
- final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
// Add ADD_ON
// Keep the same object for the whole test, to make sure we refresh its state before r/w calls
final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- testListener.pushExpectedEvent(NextEvent.CREATE);
- final Entitlement addOnEntitlement = entitlementApi.addEntitlement(entitlement.getBundleId(), addOnSpec, null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement addOnEntitlement = entitlementApi.addEntitlement(entitlement.getBundleId(), addOnSpec, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
try {
entitlement.uncancelEntitlement(ImmutableList.<PluginProperty>of(), callContext);
@@ -117,13 +119,13 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
// Keep the same object for the whole test, to make sure we refresh its state before r/w calls
- testListener.pushExpectedEvent(NextEvent.CREATE);
- final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
testListener.pushExpectedEvent(NextEvent.PHASE);
@@ -150,13 +152,13 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
// Create entitlement and check each field
- testListener.pushExpectedEvent(NextEvent.CREATE);
- final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
assertEquals(entitlement.getAccountId(), account.getId());
assertEquals(entitlement.getExternalKey(), account.getExternalKey());
@@ -242,19 +244,19 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
// Create entitlement and check each field
- testListener.pushExpectedEvent(NextEvent.CREATE);
- final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
// Add ADD_ON
final PlanPhaseSpecifier spec1 = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- testListener.pushExpectedEvent(NextEvent.CREATE);
- final Entitlement telescopicEntitlement = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), spec1, null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement telescopicEntitlement = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), spec1, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
assertEquals(telescopicEntitlement.getAccountId(), account.getId());
@@ -281,26 +283,26 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
// Create entitlement and check each field
- testListener.pushExpectedEvent(NextEvent.CREATE);
- final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
clock.addDays(1);
final LocalDate effectiveDateSpec1 = new LocalDate(clock.getUTCNow(), account.getTimeZone());
final PlanPhaseSpecifier spec1 = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- testListener.pushExpectedEvent(NextEvent.CREATE);
- final Entitlement telescopicEntitlement = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), spec1, null, effectiveDateSpec1, ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement telescopicEntitlement = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), spec1, null, effectiveDateSpec1, effectiveDateSpec1, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
// Block all entitlement in the bundle
clock.addDays(5);
- testListener.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK);
+ testListener.pushExpectedEvents(NextEvent.BLOCK);
entitlementApi.pause(baseEntitlement.getBundleId(), new LocalDate(clock.getUTCNow()), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
@@ -320,20 +322,18 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
// Try to add an ADD_ON, it should fail
try {
final PlanPhaseSpecifier spec3 = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- final Entitlement telescopicEntitlement3 = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), spec1, null, effectiveDateSpec1, ImmutableList.<PluginProperty>of(), callContext);
+ final Entitlement telescopicEntitlement3 = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), spec1, null, effectiveDateSpec1, effectiveDateSpec1, false, ImmutableList.<PluginProperty>of(), callContext);
} catch (EntitlementApiException e) {
assertEquals(e.getCode(), ErrorCode.SUB_GET_NO_SUCH_BASE_SUBSCRIPTION.getCode());
}
clock.addDays(3);
- testListener.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK);
+ testListener.pushExpectedEvents(NextEvent.BLOCK);
entitlementApi.resume(baseEntitlement.getBundleId(), new LocalDate(clock.getUTCNow()), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
- // Verify call is idempotent : The current semantics is to post the RESUME because we went through the operation, but not the BLOCK because the DAO logic
- // filtered the event as the subscription was already resumed.
- testListener.pushExpectedEvents(NextEvent.RESUME);
+ // Verify call is idempotent
entitlementApi.resume(baseEntitlement.getBundleId(), new LocalDate(clock.getUTCNow()), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
@@ -351,18 +351,17 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
}
}
-
@Test(groups = "slow", description = "Test pause / unpause in the future")
public void testPauseUnpauseInTheFuture() throws AccountApiException, EntitlementApiException {
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
// Create entitlement
- testListener.pushExpectedEvent(NextEvent.CREATE);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
// Get the phase event out of the way
@@ -380,7 +379,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
// No event yet
assertListenerStatus();
- testListener.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK);
+ testListener.pushExpectedEvents(NextEvent.BLOCK);
clock.setDay(pauseDate);
assertListenerStatus();
@@ -388,7 +387,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
final Entitlement baseEntitlementPaused = entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext);
assertEquals(baseEntitlementPaused.getState(), EntitlementState.BLOCKED);
- testListener.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK);
+ testListener.pushExpectedEvents(NextEvent.BLOCK);
clock.setDay(resumeDate);
assertListenerStatus();
@@ -402,15 +401,20 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account accountSrc = accountApi.createAccount(getAccountData(7), callContext);
- final Account accountDesc = accountApi.createAccount(getAccountData(15), callContext);
+ final Account accountDesc = createAccount(getAccountData(15));
+ // internal context will be configured for accountSrc
+ final Account accountSrc = createAccount(getAccountData(7));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- // Create entitlement
- testListener.pushExpectedEvent(NextEvent.CREATE);
- final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(accountSrc.getId(), spec, accountSrc.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ // Create entitlement (with migrated flag so we can check later that transferred subscription is in right status)
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(accountSrc.getId(), spec, accountSrc.getExternalKey(), null, null, null, true, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
+ assertEquals(baseEntitlement.getSourceType(), EntitlementSourceType.MIGRATED);
+ // Again to make sure this flag is correctly wrote/set
+ assertEquals(entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext).getSourceType(), EntitlementSourceType.MIGRATED);
+
final DateTime ctd = clock.getUTCNow().plusDays(30).plusMonths(1);
testListener.pushExpectedEvent(NextEvent.PHASE);
@@ -421,7 +425,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
// Transfer bundle to dest account
final LocalDate effectiveDate = new LocalDate(clock.getUTCNow(), accountSrc.getTimeZone());
- testListener.pushExpectedEvents(NextEvent.TRANSFER, NextEvent.BLOCK);
+ testListener.pushExpectedEvents(NextEvent.TRANSFER, NextEvent.BLOCK, NextEvent.BLOCK);
final UUID newBundleId = entitlementApi.transferEntitlementsOverrideBillingPolicy(accountSrc.getId(), accountDesc.getId(), baseEntitlement.getExternalKey(), effectiveDate, BillingActionPolicy.END_OF_TERM, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
@@ -436,6 +440,8 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
assertEquals(newBaseEntitlement.getState(), EntitlementState.ACTIVE);
assertEquals(newBaseEntitlement.getEffectiveStartDate(), effectiveDate);
assertEquals(newBaseEntitlement.getEffectiveEndDate(), null);
+ assertEquals(newBaseEntitlement.getSourceType(), EntitlementSourceType.TRANSFERRED);
+
}
@Test(groups = "slow")
@@ -443,19 +449,19 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
// Create entitlement and check each field
- testListener.pushExpectedEvent(NextEvent.CREATE);
- final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
clock.addDays(5);
testListener.pushExpectedEvents(NextEvent.BLOCK);
- entitlementApi.setBlockingState(baseEntitlement.getBundleId(), "BLOCK", "foo", new LocalDate(clock.getUTCNow()), true, true, true, ImmutableList.<PluginProperty>of(), callContext);
+ entitlementApi.setBlockingState(baseEntitlement.getBundleId(), "BLOCK", "foo", null, true, true, true, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
List<Entitlement> bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(baseEntitlement.getBundleId(), callContext);
@@ -467,20 +473,18 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
assertTrue(blockingState.isBlockChange());
assertTrue(blockingState.isBlockEntitlement());
-
- // Check unblocking on another service will not bring the sate back to ACTIVE
+ // Check unblocking on another service will not bring the state back to ACTIVE
clock.addDays(1);
testListener.pushExpectedEvents(NextEvent.BLOCK);
- entitlementApi.setBlockingState(baseEntitlement.getBundleId(), "UNBLOCK", "bar", new LocalDate(clock.getUTCNow()), false, false, false, ImmutableList.<PluginProperty>of(), callContext);
+ entitlementApi.setBlockingState(baseEntitlement.getBundleId(), "UNBLOCK", "bar", null, false, false, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(baseEntitlement.getBundleId(), callContext);
assertEquals(bundleEntitlements.size(), 1);
assertEquals(bundleEntitlements.get(0).getState(), EntitlementState.BLOCKED);
-
testListener.pushExpectedEvents(NextEvent.BLOCK);
- entitlementApi.setBlockingState(baseEntitlement.getBundleId(), "UNBLOCK", "foo", new LocalDate(clock.getUTCNow()), false, false, false, ImmutableList.<PluginProperty>of(), callContext);
+ entitlementApi.setBlockingState(baseEntitlement.getBundleId(), "UNBLOCK", "foo", null, false, false, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(baseEntitlement.getBundleId(), callContext);
@@ -491,7 +495,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
clock.addDays(1);
testListener.pushExpectedEvents(NextEvent.BLOCK);
- entitlementApi.setBlockingState(baseEntitlement.getBundleId(), "BLOCK", "foo", new LocalDate(clock.getUTCNow()), true, true, true, ImmutableList.<PluginProperty>of(), callContext);
+ entitlementApi.setBlockingState(baseEntitlement.getBundleId(), "BLOCK", "foo", null, true, true, true, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(baseEntitlement.getBundleId(), callContext);
@@ -500,7 +504,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
// Same day but happened after so should take precedence
testListener.pushExpectedEvents(NextEvent.BLOCK);
- entitlementApi.setBlockingState(baseEntitlement.getBundleId(), "UNBLOCK", "foo", new LocalDate(clock.getUTCNow()), false, false, false, ImmutableList.<PluginProperty>of(), callContext);
+ entitlementApi.setBlockingState(baseEntitlement.getBundleId(), "UNBLOCK", "foo", null, false, false, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(baseEntitlement.getBundleId(), callContext);
@@ -508,21 +512,19 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
assertEquals(bundleEntitlements.get(0).getState(), EntitlementState.ACTIVE);
}
-
-
- @Test(groups = "slow")
+ @Test(groups = "slow")
public void testCreateEntitlementInThePast() throws AccountApiException, EntitlementApiException, SubscriptionBaseApiException {
final LocalDate initialDate = new LocalDate(2013, 8, 7);
final LocalDate clockDate = new LocalDate(2013, 10, 7);
clock.setDay(clockDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
// Keep the same object for the whole test, to make sure we refresh its state before r/w calls
- testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.PHASE);
- final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.PHASE);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
assertEquals(entitlement.getAccountId(), account.getId());
@@ -561,7 +563,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Cleaning", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
@@ -574,8 +576,8 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
specifierList.add(baseEntitlementSpecifier);
specifierList.add(addOnEntitlementSpecifier);
- testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.CREATE);
- final Entitlement entitlement = entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
assertNotNull(entitlement);
@@ -599,7 +601,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Invalid", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
@@ -613,7 +615,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
specifierList.add(addOnEntitlementSpecifier);
try {
- entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
fail();
} catch (EntitlementApiException e) {
assertEquals(e.getMessage(), "Could not find any product named 'Invalid'");
@@ -629,7 +631,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Cleaning", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Bullets", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
@@ -643,7 +645,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
specifierList.add(addOnEntitlementSpecifier2);
try {
- entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
fail();
} catch (EntitlementApiException e) {
assertEquals(e.getMessage(), "Missing Base Subscription.");
@@ -651,7 +653,146 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
final List<Entitlement> allEntitlementsForAccount = entitlementApi.getAllEntitlementsForAccountId(account.getId(), callContext);
assertTrue(allEntitlementsForAccount.size() == 0);
+ }
+
+
+ @Test(groups = "slow")
+ public void testCreateBaseWithDifferentInTheFuture() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
+ final LocalDate initialDate = new LocalDate(2013, 8, 7);
+ clock.setDay(initialDate);
+
+ final Account account = createAccount(getAccountData(7));
+
+ final LocalDate entitlementDate = initialDate.plusDays(3);
+ final LocalDate billingDate = initialDate.plusDays(5);
+
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, entitlementDate, billingDate, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ assertEquals(entitlement.getState(), EntitlementState.PENDING);
+ assertEquals(entitlement.getEffectiveStartDate(), entitlementDate);
+
+ testListener.pushExpectedEvents(NextEvent.BLOCK);
+ clock.addDays(3);
+ assertListenerStatus();
+
+ testListener.pushExpectedEvents(NextEvent.CREATE);
+ clock.addDays(2);
+ assertListenerStatus();
+ }
+
+ @Test(groups = "slow")
+ public void testCreateBaseWithEntitlementInTheFuture() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
+ final LocalDate initialDate = new LocalDate(2013, 8, 7);
+ clock.setDay(initialDate);
+
+ final Account account = createAccount(getAccountData(7));
+
+ final LocalDate entitlementDate = initialDate.plusDays(3);
+ final LocalDate billingDate = null;
+
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+ testListener.pushExpectedEvents(NextEvent.CREATE);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, entitlementDate, billingDate, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ assertEquals(entitlement.getState(), EntitlementState.PENDING);
+ assertEquals(entitlement.getEffectiveStartDate(), entitlementDate);
+
+ testListener.pushExpectedEvents(NextEvent.BLOCK);
+ clock.addDays(3);
+ assertListenerStatus();
+
+ final Entitlement entitlementActive = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
+ assertEquals(entitlementActive.getState(), EntitlementState.ACTIVE);
+ }
+
+ @Test(groups = "slow")
+ public void testCreateBaseWithBillingInTheFuture() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
+ final LocalDate initialDate = new LocalDate(2013, 8, 7);
+ clock.setDay(initialDate);
+
+ final Account account = createAccount(getAccountData(7));
+
+ final LocalDate entitlementDate = null;
+ final LocalDate billingDate = initialDate.plusDays(5);
+
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+ testListener.pushExpectedEvents(NextEvent.BLOCK);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, entitlementDate, billingDate, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
+ assertEquals(entitlement.getEffectiveStartDate(), initialDate);
+
+ testListener.pushExpectedEvents(NextEvent.CREATE);
+ clock.addDays(5);
+ assertListenerStatus();
+ }
+
+ @Test(groups = "slow")
+ public void testCreateBaseWithDifferentInThePast() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
+ final LocalDate initialDate = new LocalDate(2013, 8, 7);
+ clock.setDay(initialDate);
+
+ final Account account = createAccount(getAccountData(7));
+
+ final LocalDate entitlementDate = initialDate.minusDays(3);
+ final LocalDate billingDate = initialDate.minusDays(5);
+
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+ testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.CREATE);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, entitlementDate, billingDate, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
+ assertEquals(entitlement.getEffectiveStartDate(), entitlementDate);
+ }
+
+ @Test(groups = "slow")
+ public void testCreateBaseWithEntitlementInThePast() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
+ final LocalDate initialDate = new LocalDate(2013, 8, 7);
+ clock.setDay(initialDate);
+
+ final Account account = createAccount(getAccountData(7));
+
+ final LocalDate entitlementDate = initialDate.minusDays(3);
+ final LocalDate billingDate = null;
+
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+ testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.CREATE);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, entitlementDate, billingDate, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
+ assertEquals(entitlement.getEffectiveStartDate(), entitlementDate);
+ }
+
+ @Test(groups = "slow")
+ public void testCreateBaseWithBillingInThePast() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
+ final LocalDate initialDate = new LocalDate(2013, 8, 7);
+ clock.setDay(initialDate);
+
+ final Account account = createAccount(getAccountData(7));
+
+ final LocalDate entitlementDate = null;
+ final LocalDate billingDate = initialDate.minusDays(5);
+
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+ testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.CREATE);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, entitlementDate, billingDate, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
+ assertEquals(entitlement.getEffectiveStartDate(), initialDate);
}
}
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionApi.java
index 474eb3e..171be11 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionApi.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionApi.java
@@ -16,36 +16,39 @@
package org.killbill.billing.entitlement.api;
+import java.util.Iterator;
import java.util.List;
import java.util.UUID;
+import org.joda.time.DateTime;
import org.joda.time.LocalDate;
-import org.killbill.billing.catalog.api.BillingActionPolicy;
-import org.killbill.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
-import org.killbill.billing.payment.api.PluginProperty;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
import org.killbill.billing.ErrorCode;
+import org.killbill.billing.OrderingType;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountApiException;
import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.catalog.api.BillingActionPolicy;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
import org.killbill.billing.catalog.api.PriceListSet;
import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.EntitlementService;
import org.killbill.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
import org.killbill.billing.junction.DefaultBlockingState;
+import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.util.api.AuditLevel;
import org.killbill.billing.util.audit.AuditLog;
import org.killbill.billing.util.audit.ChangeType;
+import org.testng.Assert;
+import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbeddedDB {
@@ -54,13 +57,20 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.CREATE, NextEvent.BLOCK);
- final Entitlement entitlement1 = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
- // Sleep 1 sec so created date are apparts from each other and ordering in the bundle does not default on the UUID which is random.
- try {Thread.sleep(1000); } catch (InterruptedException ignore) {};
- final Entitlement entitlement2 = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement entitlement1 = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
+ // Sleep 1 sec so created date are apart from each other and ordering in the bundle does not default on the UUID which is random.
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException ignore) {
+ }
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement entitlement2 = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ testListener.pushExpectedEvents(NextEvent.BLOCK);
entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, "stateName", "service", false, false, false, clock.getUTCNow()),
internalCallContextFactory.createInternalCallContext(account.getId(), callContext));
assertListenerStatus();
@@ -97,13 +107,13 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
// Create entitlement and check each field
- testListener.pushExpectedEvent(NextEvent.CREATE);
- final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, externalKey, null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, externalKey, null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
assertEquals(entitlement.getAccountId(), account.getId());
assertEquals(entitlement.getExternalKey(), externalKey);
@@ -136,8 +146,8 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
final PlanPhaseSpecifier spec2 = new PlanPhaseSpecifier("Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
// Create entitlement and check each field
- testListener.pushExpectedEvent(NextEvent.CREATE);
- final Entitlement entitlement2 = entitlementApi.createBaseEntitlement(account.getId(), spec2, externalKey, null, new LocalDate(clock.getUTCNow(), account.getTimeZone()), ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement entitlement2 = entitlementApi.createBaseEntitlement(account.getId(), spec2, externalKey, null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
assertEquals(entitlement2.getAccountId(), account.getId());
assertEquals(entitlement2.getExternalKey(), externalKey);
@@ -165,9 +175,9 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
clock.addDays(3);
- final Account account2 = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account2 = createAccount(getAccountData(7));
- testListener.pushExpectedEvents(NextEvent.TRANSFER, NextEvent.CANCEL, NextEvent.BLOCK);
+ testListener.pushExpectedEvents(NextEvent.TRANSFER, NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.BLOCK);
entitlementApi.transferEntitlements(account.getId(), account2.getId(), externalKey, new LocalDate(clock.getUTCNow(), account.getTimeZone()), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
@@ -203,12 +213,12 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
// Create entitlement
- testListener.pushExpectedEvent(NextEvent.CREATE);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
// Get the phase event out of the way
@@ -219,14 +229,14 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
final LocalDate pauseDate = new LocalDate(2013, 9, 17);
entitlementApi.pause(baseEntitlement.getBundleId(), pauseDate, ImmutableList.<PluginProperty>of(), callContext);
- testListener.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK);
+ testListener.pushExpectedEvents(NextEvent.BLOCK);
clock.setDay(pauseDate);
assertListenerStatus();
final LocalDate resumeDate = new LocalDate(2013, 12, 24);
entitlementApi.resume(baseEntitlement.getBundleId(), resumeDate, ImmutableList.<PluginProperty>of(), callContext);
- testListener.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK);
+ testListener.pushExpectedEvents(NextEvent.BLOCK);
clock.setDay(resumeDate);
assertListenerStatus();
@@ -251,7 +261,6 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
checkSubscriptionEventAuditLog(transitions, 8, SubscriptionEventType.STOP_BILLING);
}
-
@Test(groups = "slow")
public void testSubscriptionCreationWithFutureDate() throws AccountApiException, SubscriptionApiException, EntitlementApiException {
final String externalKey = "vritti";
@@ -259,7 +268,7 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
@@ -267,7 +276,7 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
final LocalDate effectiveDate = initialDate.plusMonths(1);
// Create entitlement and check each field
- final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, externalKey, null, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, externalKey, null, effectiveDate, effectiveDate, false, ImmutableList.<PluginProperty>of(), callContext);
final Subscription subscription = subscriptionApi.getSubscriptionForEntitlementId(entitlement.getId(), callContext);
@@ -276,35 +285,30 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
assertEquals(events.get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
assertEquals(events.get(0).getEffectiveDate().compareTo(effectiveDate), 0);
- assertEquals(events.get(0).getRequestedDate().compareTo(effectiveDate), 0);
assertEquals(events.get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
assertEquals(events.get(1).getEffectiveDate().compareTo(effectiveDate), 0);
- assertEquals(events.get(1).getRequestedDate().compareTo(effectiveDate), 0);
assertEquals(events.get(2).getSubscriptionEventType(), SubscriptionEventType.PHASE);
assertEquals(events.get(2).getEffectiveDate().compareTo(effectiveDate.plusMonths(1)), 0);
- assertEquals(events.get(2).getRequestedDate().compareTo(effectiveDate.plusMonths(1)), 0);
assertListenerStatus();
-
}
-
@Test(groups = "slow")
public void testCancelFutureSubscription() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
final LocalDate futureDate = new LocalDate(2013, 9, 1);
// No CREATE event as this is set in the future
- final Entitlement createdEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, futureDate, ImmutableList.<PluginProperty>of(), callContext);
+ final Entitlement createdEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, futureDate, futureDate, false, ImmutableList.<PluginProperty>of(), callContext);
assertEquals(createdEntitlement.getEffectiveStartDate().compareTo(futureDate), 0);
assertEquals(createdEntitlement.getEffectiveEndDate(), null);
@@ -321,6 +325,98 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
assertListenerStatus();
}
+ @Test(groups = "slow")
+ public void testAddBlockingState() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
+
+ final LocalDate initialDate = new LocalDate(2013, 8, 7);
+ clock.setDay(initialDate);
+
+ final Account account = accountApi.createAccount(getAccountData(7), callContext);
+
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement createdEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
+
+ final Iterable<BlockingState> iterableForCreateState = subscriptionApi.getBlockingStates(account.getId(), ImmutableList.of(BlockingStateType.SUBSCRIPTION), null, OrderingType.ASCENDING, SubscriptionApi.ALL_EVENTS, callContext);
+ assertTrue(iterableForCreateState.iterator().hasNext());
+ final BlockingState createState = iterableForCreateState.iterator().next();
+ assertEquals(createState.getService(), EntitlementService.ENTITLEMENT_SERVICE_NAME);
+ assertListenerStatus();
+
+ testListener.pushExpectedEvent(NextEvent.BLOCK);
+ final BlockingState state1 = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, "accountBlock", "svc1", false, true, false, clock.getUTCNow());
+ subscriptionApi.addBlockingState(state1, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ Entitlement updateEntitlement = entitlementApi.getEntitlementForId(createdEntitlement.getId(), callContext);
+ Assert.assertEquals(updateEntitlement.getState(), EntitlementState.BLOCKED);
+
+ clock.addDays(1);
+
+ testListener.pushExpectedEvent(NextEvent.BLOCK);
+ final BlockingState state2 = new DefaultBlockingState(createdEntitlement.getId(), BlockingStateType.SUBSCRIPTION, "subscriptionBlock", "svc2", false, false, false, clock.getUTCNow());
+ subscriptionApi.addBlockingState(state2, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ // Still blocked because this is a different service
+ updateEntitlement = entitlementApi.getEntitlementForId(createdEntitlement.getId(), callContext);
+ Assert.assertEquals(updateEntitlement.getState(), EntitlementState.BLOCKED);
+
+ // Now we remove the blocking state for the same service but at the SUBSCRIPTION level
+ testListener.pushExpectedEvent(NextEvent.BLOCK);
+ final BlockingState state3 = new DefaultBlockingState(createdEntitlement.getId(), BlockingStateType.SUBSCRIPTION, "subscriptionUnBlock", "svc1", false, false, false, clock.getUTCNow());
+ subscriptionApi.addBlockingState(state3, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ updateEntitlement = entitlementApi.getEntitlementForId(createdEntitlement.getId(), callContext);
+ Assert.assertEquals(updateEntitlement.getState(), EntitlementState.BLOCKED);
+
+ final DateTime futureEffectiveDate = clock.getUTCNow().plusDays(1);
+ final BlockingState state4 = new DefaultBlockingState(createdEntitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, "blockBilling", "svc1", true, false, false, futureEffectiveDate);
+ subscriptionApi.addBlockingState(state4, ImmutableList.<PluginProperty>of(), callContext);
+
+ final Iterable<BlockingState> blockingStates1 = subscriptionApi.getBlockingStates(account.getId(), ImmutableList.of(BlockingStateType.ACCOUNT, BlockingStateType.SUBSCRIPTION), ImmutableList.of("svc1", "svc2"), OrderingType.ASCENDING, SubscriptionApi.PAST_OR_PRESENT_EVENTS, callContext);
+ verifyBlockingStates(blockingStates1, ImmutableList.<BlockingState>of(state1, state2, state3));
+
+ final Iterable<BlockingState> blockingStates2 = subscriptionApi.getBlockingStates(account.getId(), ImmutableList.of(BlockingStateType.SUBSCRIPTION), ImmutableList.of("svc1", "svc2"), OrderingType.DESCENDING, SubscriptionApi.PAST_OR_PRESENT_EVENTS, callContext);
+ verifyBlockingStates(blockingStates2, ImmutableList.<BlockingState>of(state3, state2));
+
+ final Iterable<BlockingState> blockingStates3 = subscriptionApi.getBlockingStates(account.getId(), ImmutableList.of(BlockingStateType.SUBSCRIPTION), ImmutableList.of("svc2"), OrderingType.DESCENDING, SubscriptionApi.PAST_OR_PRESENT_EVENTS, callContext);
+ verifyBlockingStates(blockingStates3, ImmutableList.<BlockingState>of(state2));
+
+ final Iterable<BlockingState> blockingStates4 = subscriptionApi.getBlockingStates(account.getId(), null, null, OrderingType.DESCENDING, SubscriptionApi.ALL_EVENTS, callContext);
+ verifyBlockingStates(blockingStates4, ImmutableList.<BlockingState>of(state4, state3, state2, state1, createState));
+
+ final Iterable<BlockingState> blockingStates5 = subscriptionApi.getBlockingStates(account.getId(), ImmutableList.of(BlockingStateType.SUBSCRIPTION_BUNDLE), null, OrderingType.ASCENDING, SubscriptionApi.FUTURE_EVENTS, callContext);
+ verifyBlockingStates(blockingStates5, ImmutableList.<BlockingState>of(state4));
+
+ testListener.pushExpectedEvent(NextEvent.BLOCK);
+ clock.addDays(1);
+ assertListenerStatus();
+
+ final Iterable<BlockingState> blockingStates6 = subscriptionApi.getBlockingStates(account.getId(), null, null, OrderingType.ASCENDING, SubscriptionApi.PAST_OR_PRESENT_EVENTS, callContext);
+ verifyBlockingStates(blockingStates6, ImmutableList.<BlockingState>of(createState, state1, state2, state3, state4));
+
+ }
+
+ private void verifyBlockingStates(final Iterable<BlockingState> result, final List<BlockingState> expected) {
+ int i = 0;
+ final Iterator<BlockingState> iterator = result.iterator();
+ while (iterator.hasNext()) {
+ final BlockingState cur = iterator.next();
+ final BlockingState expectedItem = expected.get(i);
+ assertEquals(cur.isBlockBilling(), expectedItem.isBlockBilling());
+ assertEquals(cur.isBlockEntitlement(), expectedItem.isBlockEntitlement());
+ assertEquals(cur.isBlockChange(), expectedItem.isBlockChange());
+ assertEquals(cur.getService(), expectedItem.getService());
+ assertEquals(cur.getStateName(), expectedItem.getStateName());
+ assertEquals(cur.getBlockedId(), expectedItem.getBlockedId());
+ assertEquals(cur.getEffectiveDate().compareTo(expectedItem.getEffectiveDate()), 0);
+ i++;
+ }
+ assertEquals(i, expected.size());
+ }
private void checkSubscriptionEventAuditLog(final List<SubscriptionEvent> transitions, final int idx, final SubscriptionEventType expectedType) {
assertEquals(transitions.get(idx).getSubscriptionEventType(), expectedType);
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
index 7c67a0e..c4dcbd3 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -60,8 +62,8 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
public class TestSubscriptionBundleTimeline extends DefaultSubscriptionBundleTimeline {
- public TestSubscriptionBundleTimeline(final DateTimeZone accountTimeZone, final UUID accountId, final UUID bundleId, final String externalKey, final Iterable<Entitlement> entitlements) {
- super(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ public TestSubscriptionBundleTimeline(final UUID accountId, final UUID bundleId, final String externalKey, final Iterable<Entitlement> entitlements) {
+ super(accountId, bundleId, externalKey, entitlements, internalCallContext);
}
public SubscriptionEvent createEvent(final UUID subscriptionId, final SubscriptionEventType type, final DateTime effectiveDate) {
@@ -84,14 +86,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
null,
null,
null,
- null);
+ internalCallContext);
}
}
@Test(groups = "fast")
public void testReOrderSubscriptionEventsOnInvalidOrder1() {
- final TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, null, new ArrayList<Entitlement>());
+ final TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, new ArrayList<Entitlement>());
final List<SubscriptionEvent> events = new ArrayList<SubscriptionEvent>();
final UUID subscriptionId = UUID.randomUUID();
@@ -111,7 +113,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast")
public void testReOrderSubscriptionEventsOnInvalidOrder2() {
- final TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, null, new ArrayList<Entitlement>());
+ final TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, new ArrayList<Entitlement>());
final List<SubscriptionEvent> events = new ArrayList<SubscriptionEvent>();
final UUID subscriptionId = UUID.randomUUID();
@@ -131,7 +133,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast")
public void testReOrderSubscriptionEventsOnInvalidOrder3() {
- final TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, null, new ArrayList<Entitlement>());
+ final TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, new ArrayList<Entitlement>());
final List<SubscriptionEvent> events = new ArrayList<SubscriptionEvent>();
final UUID subscriptionId = UUID.randomUUID();
@@ -151,7 +153,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast")
public void testReOrderSubscriptionEventsOnInvalidOrderAndDifferentSubscriptionsSameDates1() {
- final TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, null, new ArrayList<Entitlement>());
+ final TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, new ArrayList<Entitlement>());
final List<SubscriptionEvent> events = new ArrayList<SubscriptionEvent>();
final UUID subscriptionId = UUID.fromString("60b64e0c-cefd-48c3-8de9-c731a9558165");
@@ -178,7 +180,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast")
public void testReOrderSubscriptionEventsOnInvalidOrderAndDifferentSubscriptionsSameDates2() {
- final TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, null, new ArrayList<Entitlement>());
+ final TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, new ArrayList<Entitlement>());
final List<SubscriptionEvent> events = new ArrayList<SubscriptionEvent>();
final UUID subscriptionId = UUID.fromString("35b3b340-31b2-46ea-b062-e9fc9fab3bc9");
@@ -205,7 +207,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast")
public void testReOrderSubscriptionEventsOnInvalidOrderAndDifferentSubscriptionsDates() {
- final TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, null, new ArrayList<Entitlement>());
+ final TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, new ArrayList<Entitlement>());
final List<SubscriptionEvent> events = new ArrayList<SubscriptionEvent>();
final UUID subscriptionId = UUID.randomUUID();
@@ -243,7 +245,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast")
public void testReOrderSubscriptionEventsOnCorrectOrder() {
- final TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, null, new ArrayList<Entitlement>());
+ final TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, new ArrayList<Entitlement>());
final List<SubscriptionEvent> events = new ArrayList<SubscriptionEvent>();
final UUID subscriptionId = UUID.randomUUID();
@@ -262,7 +264,17 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
}
@Test(groups = "fast")
- public void testOneEntitlementNoBlockingStates() throws CatalogApiException {
+ public void testOneSimpleEntitlement() throws CatalogApiException {
+ testOneSimpleEntitlementImpl(false);
+ }
+
+ @Test(groups = "fast")
+ public void testOneSimpleEntitlementWithRegression() throws CatalogApiException {
+ testOneSimpleEntitlementImpl(true);
+ }
+
+
+ private void testOneSimpleEntitlementImpl(boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
clock.setDay(new LocalDate(2013, 1, 1));
final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -270,6 +282,8 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final UUID bundleId = UUID.randomUUID();
final String externalKey = "foo";
+ final List<BlockingState> blockingStates = new ArrayList<BlockingState>();
+
final UUID entitlementId = UUID.randomUUID();
final List<SubscriptionBaseTransition> allTransitions = new ArrayList<SubscriptionBaseTransition>();
@@ -279,6 +293,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
allTransitions.add(tr1);
+ if (!regressionFlagForOlderVersionThan_0_17_X) {
+ final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+ blockingStates.add(bsCreate);
+ }
+
+
effectiveDate = effectiveDate.plusDays(30);
clock.addDays(30);
final SubscriptionBaseTransition tr2 = createTransition(entitlementId, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow(), "trial", "phase");
@@ -290,10 +312,10 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
allTransitions.add(tr3);
final List<Entitlement> entitlements = new ArrayList<Entitlement>();
- final Entitlement entitlement = createEntitlement(entitlementId, allTransitions);
+ final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
entitlements.add(entitlement);
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountId, bundleId, externalKey, entitlements, internalCallContext);
assertEquals(timeline.getAccountId(), accountId);
assertEquals(timeline.getBundleId(), bundleId);
@@ -318,7 +340,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
assertEquals(events.get(3).getServiceName(), EntitlementOrderingBase.BILLING_SERVICE_NAME);
assertNull(events.get(0).getPrevPhase());
- assertEquals(events.get(0).getNextPhase().getName(), "trial");
assertNull(events.get(1).getPrevPhase());
assertEquals(events.get(1).getNextPhase().getName(), "trial");
assertEquals(events.get(2).getPrevPhase().getName(), "trial");
@@ -327,8 +348,18 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
assertNull(events.get(3).getNextPhase());
}
+
+
@Test(groups = "fast")
public void testCancelBundleBeforeSubscription() throws CatalogApiException {
+ testCancelBundleBeforeSubscriptionImpl(false);
+ }
+ @Test(groups = "fast")
+ public void testCancelBundleBeforeSubscriptionWithRegression() throws CatalogApiException {
+ testCancelBundleBeforeSubscriptionImpl(true);
+ }
+
+ private void testCancelBundleBeforeSubscriptionImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
clock.setDay(new LocalDate(2013, 1, 1));
final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -346,6 +377,13 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
allTransitions.add(tr1);
+ if (!regressionFlagForOlderVersionThan_0_17_X) {
+ final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+ blockingStates.add(bsCreate);
+ }
+
// Block the bundle before the subscription
effectiveDate = effectiveDate.plusDays(15);
clock.addDays(15);
@@ -364,7 +402,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
entitlements.add(entitlement);
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountId, bundleId, externalKey, entitlements, internalCallContext);
assertEquals(timeline.getAccountId(), accountId);
assertEquals(timeline.getBundleId(), bundleId);
@@ -389,17 +427,24 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
assertEquals(events.get(3).getServiceName(), EntitlementOrderingBase.BILLING_SERVICE_NAME);
assertNull(events.get(0).getPrevPhase());
- assertEquals(events.get(0).getNextPhase().getName(), "trial");
assertNull(events.get(1).getPrevPhase());
assertEquals(events.get(1).getNextPhase().getName(), "trial");
- assertEquals(events.get(2).getPrevPhase().getName(), "trial");
- assertNull(events.get(2).getNextPhase());
assertEquals(events.get(3).getPrevPhase().getName(), "trial");
assertNull(events.get(3).getNextPhase());
}
@Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/135")
public void testOneEntitlementWithPauseResume() throws CatalogApiException {
+ testOneEntitlementWithPauseResumeImpl(false);
+ }
+
+ @Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/135")
+ public void testOneEntitlementWithPauseResumeWithRegression() throws CatalogApiException {
+ testOneEntitlementWithPauseResumeImpl(true);
+ }
+
+
+ private void testOneEntitlementWithPauseResumeImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
clock.setDay(new LocalDate(2013, 1, 1));
final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -417,6 +462,15 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
allTransitions.add(tr1);
+ if (!regressionFlagForOlderVersionThan_0_17_X) {
+ final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+
+ blockingStates.add(bsCreate);
+ }
+
+
effectiveDate = effectiveDate.plusDays(30);
clock.addDays(30);
final SubscriptionBaseTransition tr2 = createTransition(entitlementId, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow(), "trial", "phase");
@@ -459,7 +513,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
entitlements.add(entitlement);
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountId, bundleId, externalKey, entitlements, internalCallContext);
assertEquals(timeline.getAccountId(), accountId);
assertEquals(timeline.getBundleId(), bundleId);
@@ -505,7 +559,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
assertEquals(events.get(8).getServiceName(), service);
assertNull(events.get(0).getPrevPhase());
- assertEquals(events.get(0).getNextPhase().getName(), "trial");
assertNull(events.get(1).getPrevPhase());
assertEquals(events.get(1).getNextPhase().getName(), "trial");
@@ -528,6 +581,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/147 and https://github.com/killbill/killbill/issues/148")
public void testOneEntitlementWithOverduePauseThenCancel() throws CatalogApiException {
+ testOneEntitlementWithOverduePauseThenCancelImpl(false);
+ }
+
+ public void testOneEntitlementWithOverduePauseThenCancelWithRegression() throws CatalogApiException {
+ testOneEntitlementWithOverduePauseThenCancelImpl(true);
+ }
+
+ private void testOneEntitlementWithOverduePauseThenCancelImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
clock.setDay(new LocalDate(2013, 1, 1));
final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -545,6 +606,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
allTransitions.add(tr1);
+ if (!regressionFlagForOlderVersionThan_0_17_X) {
+ final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+
+ blockingStates.add(bsCreate);
+ }
+
effectiveDate = effectiveDate.plusDays(30);
clock.addDays(30);
final SubscriptionBaseTransition tr2 = createTransition(entitlementId, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow(), "trial", "phase");
@@ -598,7 +667,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
entitlements.add(entitlement);
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountId, bundleId, externalKey, entitlements, internalCallContext);
assertEquals(timeline.getAccountId(), accountId);
assertEquals(timeline.getBundleId(), bundleId);
@@ -650,7 +719,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
assertEquals(events.get(9).getServiceName(), EntitlementOrderingBase.BILLING_SERVICE_NAME);
assertNull(events.get(0).getPrevPhase());
- assertEquals(events.get(0).getNextPhase().getName(), "trial");
assertNull(events.get(1).getPrevPhase());
assertEquals(events.get(1).getNextPhase().getName(), "trial");
@@ -677,6 +745,15 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast")
public void testOneEntitlementWithInitialBlockingState() throws CatalogApiException {
+ testOneEntitlementWithInitialBlockingStateImpl(false);
+ }
+
+ @Test(groups = "fast")
+ public void testOneEntitlementWithInitialBlockingStateWithRegression() throws CatalogApiException {
+ testOneEntitlementWithInitialBlockingStateImpl(true);
+ }
+
+ private void testOneEntitlementWithInitialBlockingStateImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
clock.setDay(new LocalDate(2013, 1, 1));
final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -701,6 +778,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
allTransitions.add(tr1);
+ if (!regressionFlagForOlderVersionThan_0_17_X) {
+ final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+
+ blockingStates.add(bsCreate);
+ }
+
effectiveDate = effectiveDate.plusDays(30);
clock.addDays(30);
final SubscriptionBaseTransition tr2 = createTransition(entitlementId, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow(), "trial", "phase");
@@ -722,7 +807,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
entitlements.add(entitlement);
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountId, bundleId, externalKey, entitlements, internalCallContext);
assertEquals(timeline.getAccountId(), accountId);
assertEquals(timeline.getBundleId(), bundleId);
@@ -750,7 +835,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
assertEquals(events.get(4).getServiceName(), EntitlementOrderingBase.BILLING_SERVICE_NAME);
assertNull(events.get(0).getPrevPhase());
- assertEquals(events.get(0).getNextPhase().getName(), "trial");
assertNull(events.get(1).getPrevPhase());
assertEquals(events.get(1).getNextPhase().getName(), "trial");
assertEquals(events.get(2).getPrevPhase().getName(), "trial");
@@ -763,6 +847,15 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast")
public void testOneEntitlementWithBlockingStatesSubscription() throws CatalogApiException {
+ testOneEntitlementWithBlockingStatesSubscriptionImpl(false);
+ }
+
+ @Test(groups = "fast")
+ public void testOneEntitlementWithBlockingStatesSubscriptionWithRegression() throws CatalogApiException {
+ testOneEntitlementWithBlockingStatesSubscriptionImpl(true);
+ }
+
+ private void testOneEntitlementWithBlockingStatesSubscriptionImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
clock.setDay(new LocalDate(2013, 1, 1));
final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -780,6 +873,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
allTransitions.add(tr1);
+ if (!regressionFlagForOlderVersionThan_0_17_X) {
+ final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+
+ blockingStates.add(bsCreate);
+ }
+
effectiveDate = effectiveDate.plusDays(30);
clock.addDays(30);
final SubscriptionBaseTransition tr2 = createTransition(entitlementId, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow(), "trial", "phase");
@@ -806,7 +907,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
entitlements.add(entitlement);
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountId, bundleId, externalKey, entitlements, internalCallContext);
final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
assertEquals(events.size(), 6);
@@ -833,7 +934,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
assertEquals(events.get(5).getServiceName(), EntitlementOrderingBase.BILLING_SERVICE_NAME);
assertNull(events.get(0).getPrevPhase());
- assertEquals(events.get(0).getNextPhase().getName(), "trial");
assertNull(events.get(1).getPrevPhase());
assertEquals(events.get(1).getNextPhase().getName(), "trial");
@@ -851,6 +951,15 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast")
public void testWithMultipleEntitlements() throws CatalogApiException {
+ testWithMultipleEntitlementsImpl(false);
+ }
+
+ @Test(groups = "fast")
+ public void testWithMultipleEntitlementsWithRegression() throws CatalogApiException {
+ testWithMultipleEntitlementsImpl(true);
+ }
+
+ private void testWithMultipleEntitlementsImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
clock.setDay(new LocalDate(2013, 1, 1));
final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -870,11 +979,27 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBaseTransition ent1Tr1 = createTransition(entitlementId1, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial1");
allTransitions1.add(ent1Tr1);
+ if (!regressionFlagForOlderVersionThan_0_17_X) {
+ final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId1, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+
+ blockingStates.add(bsCreate);
+ }
+
effectiveDate = effectiveDate.plusDays(15);
clock.addDays(15);
final SubscriptionBaseTransition ent2Tr1 = createTransition(entitlementId2, EventType.API_USER, ApiEventType.TRANSFER, requestedDate, effectiveDate, clock.getUTCNow(), null, "phase2");
allTransitions2.add(ent2Tr1);
+ if (!regressionFlagForOlderVersionThan_0_17_X ) {
+ final BlockingState bsCreate2 = new DefaultBlockingState(UUID.randomUUID(), entitlementId2, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+
+ blockingStates.add(bsCreate2);
+ }
+
effectiveDate = effectiveDate.plusDays(15);
clock.addDays(15);
final SubscriptionBaseTransition ent1Tr2 = createTransition(entitlementId1, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow(), "trial1", "phase1");
@@ -904,7 +1029,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final Entitlement entitlement2 = createEntitlement(entitlementId2, allTransitions2, blockingStates);
entitlements.add(entitlement2);
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountId, bundleId, externalKey, entitlements, internalCallContext);
final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
assertEquals(events.size(), 9);
@@ -949,7 +1074,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
assertEquals(events.get(8).getServiceName(), EntitlementOrderingBase.BILLING_SERVICE_NAME);
assertNull(events.get(0).getPrevPhase());
- assertEquals(events.get(0).getNextPhase().getName(), "trial1");
assertNull(events.get(1).getPrevPhase());
assertEquals(events.get(1).getNextPhase().getName(), "trial1");
@@ -976,6 +1100,15 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast")
public void testWithOverdueOffline() throws CatalogApiException {
+ testWithOverdueOfflineImpl(false);
+ }
+
+ @Test(groups = "fast")
+ public void testWithOverdueOfflineWithRegression() throws CatalogApiException {
+ testWithOverdueOfflineImpl(true);
+ }
+
+ private void testWithOverdueOfflineImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
clock.setDay(new LocalDate(2013, 1, 1));
final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -994,6 +1127,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
allTransitions.add(tr1);
+ if (!regressionFlagForOlderVersionThan_0_17_X) {
+ final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+
+ blockingStates.add(bsCreate);
+ }
+
effectiveDate = effectiveDate.plusDays(30);
clock.addDays(30);
final SubscriptionBaseTransition tr2 = createTransition(entitlementId, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow(), "trial", "phase");
@@ -1006,7 +1147,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
// Verify the timeline without the blocking state events
final ImmutableList<Entitlement> entitlementsWithoutBlockingStates = ImmutableList.<Entitlement>of(createEntitlement(entitlementId, allTransitions, blockingStates));
- final List<SubscriptionEvent> eventsWithoutBlockingStates = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlementsWithoutBlockingStates).getSubscriptionEvents();
+ final List<SubscriptionEvent> eventsWithoutBlockingStates = new DefaultSubscriptionBundleTimeline(accountId, bundleId, externalKey, entitlementsWithoutBlockingStates, internalCallContext).getSubscriptionEvents();
assertEquals(eventsWithoutBlockingStates.size(), 4);
assertEquals(eventsWithoutBlockingStates.get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
assertEquals(eventsWithoutBlockingStates.get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
@@ -1022,7 +1163,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
// Verify the timeline with the overdue event blocking the entitlement
final ImmutableList<Entitlement> entitlementsWithOverdueEvent = ImmutableList.<Entitlement>of(createEntitlement(entitlementId, allTransitions, blockingStates));
- final List<SubscriptionEvent> eventsWithOverdueEvent = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlementsWithOverdueEvent).getSubscriptionEvents();
+ final List<SubscriptionEvent> eventsWithOverdueEvent = new DefaultSubscriptionBundleTimeline(accountId, bundleId, externalKey, entitlementsWithOverdueEvent, internalCallContext).getSubscriptionEvents();
assertEquals(eventsWithOverdueEvent.size(), 5);
assertEquals(eventsWithOverdueEvent.get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
assertEquals(eventsWithOverdueEvent.get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
@@ -1041,7 +1182,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
entitlements.add(entitlement);
// Verify the timeline with both the overdue event and the entitlement cancel event
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountId, bundleId, externalKey, entitlements, internalCallContext);
assertEquals(timeline.getAccountId(), accountId);
assertEquals(timeline.getBundleId(), bundleId);
@@ -1072,7 +1213,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
assertEquals(events.get(5).getServiceName(), EntitlementOrderingBase.BILLING_SERVICE_NAME);
assertNull(events.get(0).getPrevPhase());
- assertEquals(events.get(0).getNextPhase().getName(), "trial");
assertNull(events.get(1).getPrevPhase());
assertEquals(events.get(1).getNextPhase().getName(), "trial");
@@ -1090,6 +1230,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/134")
public void testRemoveOverlappingBlockingStates() throws CatalogApiException {
+ testRemoveOverlappingBlockingStatesImpl(false);
+ }
+
+ public void testRemoveOverlappingBlockingStatesWithRegression() throws CatalogApiException {
+ testRemoveOverlappingBlockingStatesImpl(true);
+ }
+
+ public void testRemoveOverlappingBlockingStatesImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
clock.setDay(new LocalDate(2013, 1, 1));
final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -1107,6 +1255,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
allTransitions.add(tr1);
+ if (!regressionFlagForOlderVersionThan_0_17_X) {
+ final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+
+ blockingStates.add(bsCreate);
+ }
+
// Overlapping ENT_STATE_BLOCKED - should merge
effectiveDate = effectiveDate.plusDays(5);
clock.addDays(5);
@@ -1141,7 +1297,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
entitlements.add(entitlement);
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountId, bundleId, externalKey, entitlements, internalCallContext);
final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
assertEquals(events.size(), 4);
@@ -1162,7 +1318,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
assertEquals(events.get(3).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
assertNull(events.get(0).getPrevPhase());
- assertEquals(events.get(0).getNextPhase().getName(), "trial");
assertNull(events.get(1).getPrevPhase());
assertEquals(events.get(1).getNextPhase().getName(), "trial");
@@ -1175,6 +1330,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/149")
public void testVariousBlockingStatesAtTheSameEffectiveDate() throws CatalogApiException {
+ testVariousBlockingStatesAtTheSameEffectiveDateImpl(false);
+ }
+
+ public void testVariousBlockingStatesAtTheSameEffectiveDateWithRegression() throws CatalogApiException {
+ testVariousBlockingStatesAtTheSameEffectiveDateImpl(true);
+ }
+
+ private void testVariousBlockingStatesAtTheSameEffectiveDateImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
clock.setDay(new LocalDate(2013, 1, 1));
final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -1192,6 +1355,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
allTransitions.add(tr1);
+ if (!regressionFlagForOlderVersionThan_0_17_X) {
+ final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+
+ blockingStates.add(bsCreate);
+ }
+
// 2013-02-10
effectiveDate = effectiveDate.plusDays(40);
clock.addDays(40);
@@ -1234,7 +1405,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
entitlements.add(entitlement);
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountId, bundleId, externalKey, entitlements, internalCallContext);
final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
assertEquals(events.size(), 11);
@@ -1284,7 +1455,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
assertEquals(events.get(10).getServiceName(), overdueService);
assertNull(events.get(0).getPrevPhase());
- assertEquals(events.get(0).getNextPhase().getName(), "trial");
assertNull(events.get(1).getPrevPhase());
assertEquals(events.get(1).getNextPhase().getName(), "trial");
@@ -1325,6 +1495,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBase base = Mockito.mock(SubscriptionBase.class);
Mockito.when(base.getAllTransitions()).thenReturn(allTransitions);
Mockito.when(result.getSubscriptionBase()).thenReturn(base);
+ Mockito.when(result.getSubscriptionBase().getStartDate()).thenReturn(new DateTime(DateTimeZone.UTC));
return result;
}
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEntitlementDateHelper.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEntitlementDateHelper.java
index ae889c9..07ddabf 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEntitlementDateHelper.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEntitlementDateHelper.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -16,72 +18,66 @@
package org.killbill.billing.entitlement.api;
+import java.util.UUID;
+
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
-import org.mockito.Mockito;
+import org.killbill.billing.GuicyKillbillTestSuiteNoDB;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountApiException;
+import org.killbill.billing.entitlement.EntitlementTestSuiteNoDB;
+import org.killbill.billing.mock.MockAccountBuilder;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
-import org.killbill.billing.account.api.Account;
-import org.killbill.billing.callcontext.InternalTenantContext;
-import org.killbill.billing.entitlement.EntitlementTestSuiteNoDB;
-
import static org.testng.Assert.assertTrue;
public class TestEntitlementDateHelper extends EntitlementTestSuiteNoDB {
- private Account account;
private EntitlementDateHelper dateHelper;
@BeforeClass(groups = "fast")
public void beforeMethod() throws Exception {
super.beforeClass();
-
- account = Mockito.mock(Account.class);
- Mockito.when(accountInternalApi.getAccountByRecordId(Mockito.anyLong(), Mockito.<InternalTenantContext>any())).thenReturn(account);
- Mockito.when(accountInternalApi.getImmutableAccountDataByRecordId(Mockito.anyLong(), Mockito.<InternalTenantContext>any())).thenReturn(account);
- dateHelper = new EntitlementDateHelper(accountInternalApi, clock);
- clock.resetDeltaFromReality();;
+ dateHelper = new EntitlementDateHelper(clock);
+ clock.resetDeltaFromReality();
}
@Test(groups = "fast")
- public void testWithAccountInUtc() throws EntitlementApiException {
-
+ public void testWithAccountInUtc() throws AccountApiException, EntitlementApiException {
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate.plusDays(1));
- Mockito.when(account.getTimeZone()).thenReturn(DateTimeZone.UTC);
+ final DateTime referenceDateTime = new DateTime(2013, 1, 1, 15, 43, 25, 0, DateTimeZone.UTC);
+ createAccount(DateTimeZone.UTC, referenceDateTime);
- final DateTime refererenceDateTime = new DateTime(2013, 1, 1, 15, 43, 25, 0, DateTimeZone.UTC);
- final DateTime targetDate = dateHelper.fromLocalDateAndReferenceTime(initialDate, refererenceDateTime, internalCallContext);
+ final DateTime targetDate = dateHelper.fromLocalDateAndReferenceTime(initialDate, internalCallContext);
final DateTime expectedDate = new DateTime(2013, 8, 7, 15, 43, 25, 0, DateTimeZone.UTC);
Assert.assertEquals(targetDate, expectedDate);
}
-
@Test(groups = "fast")
- public void testWithAccountInUtcMinus8() throws EntitlementApiException {
-
+ public void testWithAccountInUtcMinus8() throws AccountApiException, EntitlementApiException {
final LocalDate inputDate = new LocalDate(2013, 8, 7);
// Current time is in the future so we don't go through logic that will default to a Clock.getUTCNow.
clock.setDay(inputDate.plusDays(3));
final DateTimeZone timeZoneUtcMinus8 = DateTimeZone.forOffsetHours(-8);
- Mockito.when(account.getTimeZone()).thenReturn(timeZoneUtcMinus8);
-
// We also use a reference time of 1, 28, 10, 0 -> DateTime in accountTimeZone will be (2013, 8, 7, 1, 28, 10)
- final DateTime refererenceDateTime = new DateTime(2013, 1, 1, 1, 28, 10, 0, DateTimeZone.UTC);
- final DateTime targetDate = dateHelper.fromLocalDateAndReferenceTime(inputDate, refererenceDateTime, internalCallContext);
+ final DateTime referenceDateTime = new DateTime(2013, 1, 1, 1, 28, 10, 0, DateTimeZone.UTC);
+
+ createAccount(timeZoneUtcMinus8, referenceDateTime);
+ final DateTime targetDate = dateHelper.fromLocalDateAndReferenceTime(inputDate, internalCallContext);
// Things to verify:
// 1. Verify the resulting DateTime brings us back into the correct LocalDate (in the account timezone)
Assert.assertEquals(new LocalDate(targetDate, timeZoneUtcMinus8), inputDate);
// 2. Verify the resulting DateTime has the same reference time as we indicated (in UTC)
- Assert.assertEquals(targetDate.toLocalTime(), refererenceDateTime.toLocalTime());
+ Assert.assertEquals(targetDate.toLocalTime(), referenceDateTime.toLocalTime());
//
// To be more specific, we should find a UTC Date, with the exact specified reference time, and with a LocalDate one day
@@ -90,26 +86,24 @@ public class TestEntitlementDateHelper extends EntitlementTestSuiteNoDB {
Assert.assertEquals(targetDate, new DateTime(2013, 8, 8, 1, 28, 10, 0, DateTimeZone.UTC));
}
-
-
@Test(groups = "fast")
- public void testWithAccountInUtcPlus5() throws EntitlementApiException {
-
+ public void testWithAccountInUtcPlus5() throws AccountApiException, EntitlementApiException {
final LocalDate inputDate = new LocalDate(2013, 8, 7);
clock.setDay(inputDate.plusDays(1));
final DateTimeZone timeZoneUtcPlus5 = DateTimeZone.forOffsetHours(+5);
- Mockito.when(account.getTimeZone()).thenReturn(timeZoneUtcPlus5);
-
// We also use a reference time of 20, 28, 10, 0 -> DateTime in accountTimeZone will be (2013, 8, 7, 20, 28, 10)
- final DateTime refererenceDateTime = new DateTime(2013, 1, 1, 20, 28, 10, 0, DateTimeZone.UTC);
- final DateTime targetDate = dateHelper.fromLocalDateAndReferenceTime(inputDate, refererenceDateTime, internalCallContext);
+ final DateTime referenceDateTime = new DateTime(2013, 1, 1, 20, 28, 10, 0, DateTimeZone.UTC);
+
+ createAccount(timeZoneUtcPlus5, referenceDateTime);
+
+ final DateTime targetDate = dateHelper.fromLocalDateAndReferenceTime(inputDate, internalCallContext);
// Things to verify:
// 1. Verify the resulting DateTime brings us back into the correct LocalDate (in the account timezone)
Assert.assertEquals(new LocalDate(targetDate, timeZoneUtcPlus5), inputDate);
// 2. Verify the resulting DateTime has the same reference time as we indicated (in UTC)
- Assert.assertEquals(targetDate.toLocalTime(), refererenceDateTime.toLocalTime());
+ Assert.assertEquals(targetDate.toLocalTime(), referenceDateTime.toLocalTime());
//
// To be more specific, we should find a UTC Date, with the exact specified reference time, and with a LocalDate one day
@@ -119,36 +113,34 @@ public class TestEntitlementDateHelper extends EntitlementTestSuiteNoDB {
}
@Test(groups = "fast")
- public void testWhereLocalDateInAccountTimeZoneContainsNow() throws EntitlementApiException {
-
- final DateTime initialNow = new DateTime(2013, 8, 22,22, 07, 01, 0, DateTimeZone.UTC);
- clock.setTime(initialNow);
-
- final LocalDate inputDate = new LocalDate(2013, 8, 22);
-
- final DateTimeZone timeZoneUtcMinus8 = DateTimeZone.forOffsetHours(-8);
- Mockito.when(account.getTimeZone()).thenReturn(timeZoneUtcMinus8);
-
- final DateTime referenceDateTimeThatDoesNotMatter = new DateTime();
- final DateTime targetDate = dateHelper.fromLocalDateAndReferenceTime(inputDate, referenceDateTimeThatDoesNotMatter, internalCallContext);
-
- final DateTime now = clock.getUTCNow();
- Assert.assertTrue(initialNow.compareTo(targetDate) <= 0);
- Assert.assertTrue(targetDate.compareTo(now) <= 0);
- }
-
-
- @Test(groups = "fast")
- public void testIsBeforeOrEqualsToday() {
-
+ public void testIsBeforeOrEqualsToday() throws AccountApiException {
clock.setTime(new DateTime(2013, 8, 7, 3, 28, 10, 0, DateTimeZone.UTC));
+
final DateTimeZone timeZoneUtcMinus8 = DateTimeZone.forOffsetHours(-8);
+ createAccount(timeZoneUtcMinus8, clock.getUTCNow());
final DateTime inputDateEquals = new DateTime(2013, 8, 6, 23, 28, 10, 0, timeZoneUtcMinus8);
// Check that our input date is greater than now
assertTrue(inputDateEquals.compareTo(clock.getUTCNow()) > 0);
// And yet since the LocalDate match the function returns true
- assertTrue(dateHelper.isBeforeOrEqualsToday(inputDateEquals, timeZoneUtcMinus8));
+ assertTrue(dateHelper.isBeforeOrEqualsToday(inputDateEquals, timeZoneUtcMinus8, internalCallContext));
+ }
+
+ private void createAccount(final DateTimeZone dateTimeZone, final DateTime referenceDateTime) throws AccountApiException {
+ final Account accountData = new MockAccountBuilder().externalKey(UUID.randomUUID().toString())
+ .timeZone(dateTimeZone)
+ .createdDate(referenceDateTime)
+ .build();
+
+ GuicyKillbillTestSuiteNoDB.createMockAccount(accountData,
+ accountUserApi,
+ accountInternalApi,
+ immutableAccountInternalApi,
+ nonEntityDao,
+ clock,
+ internalCallContextFactory,
+ callContext,
+ internalCallContext);
}
}
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEventJson.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEventJson.java
index 3422f32..dd7b6b8 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEventJson.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEventJson.java
@@ -31,7 +31,9 @@ public class TestEventJson extends EntitlementTestSuiteNoDB {
@Test(groups = "fast", description = "Test Blocking event deserialization")
public void testDefaultBlockingTransitionInternalEvent() throws Exception {
- final BlockingTransitionInternalEvent e = new DefaultBlockingTransitionInternalEvent(UUID.randomUUID(), BlockingStateType.ACCOUNT, true, false, false, true, 1L, 2L, null);
+ final BlockingTransitionInternalEvent e = new DefaultBlockingTransitionInternalEvent(UUID.randomUUID(), "state", "svc",
+ clock.getUTCNow(), BlockingStateType.ACCOUNT,
+ true, false, false, true, 1L, 2L, null);
final String json = mapper.writeValueAsString(e);
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestRegessionSubscriptionApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestRegessionSubscriptionApi.java
new file mode 100644
index 0000000..ae7f4ba
--- /dev/null
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestRegessionSubscriptionApi.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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.entitlement.api;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.killbill.billing.ErrorCode;
+import org.killbill.billing.OrderingType;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountApiException;
+import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.catalog.api.BillingActionPolicy;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.PriceListSet;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.EntitlementService;
+import org.killbill.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
+import org.killbill.billing.junction.DefaultBlockingState;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLog;
+import org.killbill.billing.util.audit.ChangeType;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+public class TestRegessionSubscriptionApi extends EntitlementTestSuiteWithEmbeddedDB {
+
+ @Test(groups = "slow", description = "Verify behavior with or without ENT_STARTED event works as expected")
+ public void testRegressionForNew_ENT_STARTED_event() throws Exception {
+
+ final LocalDate initialDate = new LocalDate(2013, 8, 7);
+ clock.setDay(initialDate);
+
+ // Start the entitlement yesterday (does not m,ake sense, but we want to check later different of behavior)
+ final LocalDate entitlementEffectiveDate = initialDate.minusDays(1);
+
+ final Account account = createAccount(getAccountData(7));
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), null, entitlementEffectiveDate, null, false, ImmutableList.<PluginProperty>of(), callContext);
+ // Because of the BlockingState event ENT_STARTED, the entitlement date should be correctly set
+ Assert.assertEquals(entitlement.getEffectiveStartDate(), entitlementEffectiveDate);
+
+ final List<SubscriptionBundle> bundles = subscriptionApi.getSubscriptionBundlesForAccountId(account.getId(), callContext);
+ Assert.assertEquals(bundles.size(), 1);
+ subscriptionBundleChecker(bundles, initialDate, entitlement, 0);
+
+ // Let's do some surgery and inactivate the ENT_STARTED BlockingState
+ final List<BlockingState> blockingStates = blockingStateDao.getBlockingState(entitlement.getId(), BlockingStateType.SUBSCRIPTION, clock.getUTCNow(), internalCallContext);
+ assertEquals(blockingStates.size(), 1);
+ assertEquals(blockingStates.get(0).getStateName(), DefaultEntitlementApi.ENT_STATE_START);
+ blockingStateDao.unactiveBlockingState(blockingStates.get(0).getId(), internalCallContext);
+
+ final Entitlement oldSchoolEntitlement = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
+ // Because the ENT_STARTED BlockingState has been invalidated, the startDate should now default to the billingDate
+ Assert.assertEquals(oldSchoolEntitlement.getEffectiveStartDate(), initialDate);
+
+ final List<SubscriptionBundle> oldSchoolBundles = subscriptionApi.getSubscriptionBundlesForAccountId(account.getId(), callContext);
+ Assert.assertEquals(oldSchoolBundles.size(), 1);
+ subscriptionBundleChecker(oldSchoolBundles, initialDate, oldSchoolEntitlement, 0);
+ }
+
+ private void subscriptionBundleChecker(final List<SubscriptionBundle> bundles, final LocalDate billingStartDate, final Entitlement entitlement, final int idx) {
+ Assert.assertEquals(bundles.get(idx).getId(), entitlement.getBundleId());
+ Assert.assertEquals(bundles.get(idx).getSubscriptions().size(), 1);
+ Assert.assertEquals(bundles.get(idx).getSubscriptions().get(0).getId(), entitlement.getId());
+ Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().size(), 3);
+ Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(0).getEffectiveDate(), entitlement.getEffectiveStartDate());
+ Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
+ Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(1).getEffectiveDate(), billingStartDate);
+ Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
+ Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(2).getEffectiveDate(), new LocalDate(2013, 9, 6));
+ Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(2).getSubscriptionEventType(), SubscriptionEventType.PHASE);
+ }
+
+}
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingApi.java
index 984955c..3867c9f 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingApi.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingApi.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -88,8 +88,7 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
final boolean blockEntitlement = false;
final boolean blockBilling = false;
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
- final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+ final Account account = createAccount(getAccountData(7));
testListener.pushExpectedEvent(NextEvent.BLOCK);
final BlockingState state1 = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, overdueStateName, service, blockChange, blockEntitlement, blockBilling, clock.getUTCNow());
@@ -118,7 +117,6 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
Assert.assertEquals(history.get(1).getStateName(), overdueStateName2);
}
-
@Test(groups = "slow")
public void testBlockingAcrossTypes() throws Exception {
@@ -130,8 +128,7 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
final boolean blockEntitlement = true;
final boolean blockBilling = false;
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
- final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+ final Account account = createAccount(getAccountData(7));
testListener.pushExpectedEvent(NextEvent.BLOCK);
final BlockingState state1 = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, stateNameBlock, service, blockChange, blockEntitlement, blockBilling, clock.getUTCNow());
@@ -140,8 +137,8 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- testListener.pushExpectedEvent(NextEvent.CREATE);
- Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, clock.getUTCToday(), ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
assertEquals(baseEntitlement.getState(), EntitlementState.BLOCKED);
@@ -149,7 +146,7 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
// Add blocking at bundle level.
clock.addDays(1);
testListener.pushExpectedEvent(NextEvent.BLOCK);
- entitlementApi.setBlockingState(baseEntitlement.getBundleId(), stateNameBlock, service, clock.getUTCToday(), blockBilling, blockEntitlement, blockChange, ImmutableList.<PluginProperty>of(), callContext);
+ entitlementApi.setBlockingState(baseEntitlement.getBundleId(), stateNameBlock, service, null, blockBilling, blockEntitlement, blockChange, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
@@ -170,7 +167,7 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
// Remove blocking at bundle level.
clock.addDays(1);
testListener.pushExpectedEvent(NextEvent.BLOCK);
- entitlementApi.setBlockingState(baseEntitlement.getBundleId(), stateNameUnBlock, service, clock.getUTCToday(), false, false, false, ImmutableList.<PluginProperty>of(), callContext);
+ entitlementApi.setBlockingState(baseEntitlement.getBundleId(), stateNameUnBlock, service, null, false, false, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
baseEntitlement = entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext);
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/dao/TestBlockingDao.java b/entitlement/src/test/java/org/killbill/billing/entitlement/dao/TestBlockingDao.java
index f078f2f..a9478c7 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/dao/TestBlockingDao.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/dao/TestBlockingDao.java
@@ -40,10 +40,7 @@ public class TestBlockingDao extends EntitlementTestSuiteWithEmbeddedDB {
@BeforeMethod(groups = "slow")
public void setUp() throws Exception {
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
-
- // Override the context with the right account record id
- internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+ final Account account = createAccount(getAccountData(7));
}
@Test(groups = "slow", description = "Check BlockingStateDao with a single service")
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/dao/TestDefaultBlockingStateDao.java b/entitlement/src/test/java/org/killbill/billing/entitlement/dao/TestDefaultBlockingStateDao.java
index 62a2919..b22454a 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/dao/TestDefaultBlockingStateDao.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/dao/TestDefaultBlockingStateDao.java
@@ -50,10 +50,7 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
@BeforeMethod(groups = "slow")
public void setUp() throws Exception {
- account = accountApi.createAccount(getAccountData(7), callContext);
-
- // Override the context with the right account record id
- internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+ account = createAccount(getAccountData(7));
}
@Test(groups = "slow", description = "Verify we don't insert extra add-on events")
@@ -62,8 +59,8 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
// things in case there are no future add-on cancellation events to add in the stream.
// See TestEntitlementUtils for a more comprehensive test
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- testListener.pushExpectedEvent(NextEvent.CREATE);
- final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, clock.getUTCToday(), ImmutableList.<PluginProperty>of(), callContext);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
final BlockingStateType type = BlockingStateType.SUBSCRIPTION;
@@ -71,14 +68,15 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
final String service = "service";
// Verify initial state
- Assert.assertEquals(blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext).size(), 0);
+ Assert.assertEquals(blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext).size(), 1);
- // Set a state
+ // Set a state in the future so no event
final DateTime stateDateTime = new DateTime(2013, 5, 6, 10, 11, 12, DateTimeZone.UTC);
final BlockingState blockingState = new DefaultBlockingState(entitlement.getId(), type, state, service, false, false, false, stateDateTime);
blockingStateDao.setBlockingStatesAndPostBlockingTransitionEvent(ImmutableMap.<BlockingState, Optional<UUID>>of(blockingState, Optional.<UUID>of(entitlement.getBundleId())), internalCallContext);
+ assertListenerStatus();
- Assert.assertEquals(blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext).size(), 1);
+ Assert.assertEquals(blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext).size(), 2);
}
// See https://github.com/killbill/killbill/issues/111
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/engine/core/TestEntitlementUtils.java b/entitlement/src/test/java/org/killbill/billing/entitlement/engine/core/TestEntitlementUtils.java
index b69e58b..4ca0b7a 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/engine/core/TestEntitlementUtils.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/engine/core/TestEntitlementUtils.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -24,19 +26,14 @@ import javax.annotation.Nullable;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
-import org.killbill.billing.payment.api.PluginProperty;
-import org.testng.Assert;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
import org.killbill.billing.account.api.Account;
import org.killbill.billing.api.TestApiListener.NextEvent;
-import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.BillingActionPolicy;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
import org.killbill.billing.catalog.api.PriceListSet;
import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.AccountEventsStreams;
import org.killbill.billing.entitlement.EntitlementService;
import org.killbill.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
import org.killbill.billing.entitlement.EventsStream;
@@ -47,7 +44,10 @@ import org.killbill.billing.entitlement.api.DefaultEntitlementApi;
import org.killbill.billing.entitlement.api.Entitlement;
import org.killbill.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
import org.killbill.billing.entitlement.api.EntitlementApiException;
-import org.killbill.billing.entitlement.dao.BlockingStateSqlDao;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
@@ -56,8 +56,6 @@ import com.google.common.collect.Iterables;
public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
- private BlockingStateSqlDao sqlDao;
- private Account account;
private DefaultEntitlement baseEntitlement;
private DefaultEntitlement addOnEntitlement;
// Dates for the base plan only
@@ -68,23 +66,21 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
@BeforeMethod(groups = "slow")
public void setUp() throws Exception {
- sqlDao = dbi.onDemand(BlockingStateSqlDao.class);
-
clock.setDay(initialDate);
- account = accountApi.createAccount(getAccountData(7), callContext);
+ final Account account = createAccount(getAccountData(7));
- // Override the context with the right account record id
- internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-
- testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.CREATE);
// Create base entitlement
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- baseEntitlement = (DefaultEntitlement) entitlementApi.createBaseEntitlement(account.getId(), baseSpec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ baseEntitlement = (DefaultEntitlement) entitlementApi.createBaseEntitlement(account.getId(), baseSpec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
// Add ADD_ON
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- addOnEntitlement = (DefaultEntitlement) entitlementApi.addEntitlement(baseEntitlement.getBundleId(), addOnSpec, null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ addOnEntitlement = (DefaultEntitlement) entitlementApi.addEntitlement(baseEntitlement.getBundleId(), addOnSpec, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
// Verify the initial state
checkFutureBlockingStatesToCancel(baseEntitlement, null, null);
@@ -269,9 +265,9 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
@Test(groups = "slow", description = "Verify we don't mix add-ons for EOT changes")
public void testChangePlanEOTWith2AddOns() throws Exception {
// Add a second ADD_ON (Laser-Scope is available, not included)
- testListener.pushExpectedEvents(NextEvent.CREATE);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
final PlanPhaseSpecifier secondAddOnSpec = new PlanPhaseSpecifier("Laser-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- final DefaultEntitlement secondAddOnEntitlement = (DefaultEntitlement) entitlementApi.addEntitlement(baseEntitlement.getBundleId(), secondAddOnSpec, null, clock.getUTCToday(), ImmutableList.<PluginProperty>of(), callContext);
+ final DefaultEntitlement secondAddOnEntitlement = (DefaultEntitlement) entitlementApi.addEntitlement(baseEntitlement.getBundleId(), secondAddOnSpec, null, clock.getUTCToday(), clock.getUTCToday(), false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
// Change plan EOT to Assault-Rifle (Telescopic-Scope is included)
@@ -283,7 +279,7 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
checkBlockingStatesDAO(changedBaseEntitlement, addOnEntitlement, baseEffectiveCancellationOrChangeDate, false);
// ...but not for the second one
final List<BlockingState> blockingStatesForSecondAddOn = blockingStatesForBlockedId(secondAddOnEntitlement.getId());
- Assert.assertEquals(blockingStatesForSecondAddOn.size(), 0);
+ Assert.assertEquals(blockingStatesForSecondAddOn.size(), 1);
}
@Test(groups = "slow", description = "Verify add-ons blocking states are added for IMM change plans")
@@ -328,9 +324,9 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
@Test(groups = "slow", description = "Verify add-ons are not active after base entitlement is cancelled")
public void testCancelAddonsWhenBaseEntitlementIsCancelled() throws Exception {
// Add a second ADD_ON
- testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.PHASE);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.PHASE);
final PlanPhaseSpecifier addOn2Spec = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- final Entitlement addOn2Entitlement = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), addOn2Spec, null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ final Entitlement addOn2Entitlement = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), addOn2Spec, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
// Date prior to the base cancellation date to verify it is not impacted by the base cancellation (in contrary to the second add-on)
@@ -426,22 +422,22 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
// Test the DAO
private void checkBlockingStatesDAO(final DefaultEntitlement baseEntitlement, final DefaultEntitlement addOnEntitlement, final LocalDate effectiveBaseCancellationDate, final LocalDate effectiveAddOnCancellationDate, final boolean isBaseCancelled) {
final List<BlockingState> blockingStatesForBaseEntitlement = blockingStatesForBlockedId(baseEntitlement.getId());
- Assert.assertEquals(blockingStatesForBaseEntitlement.size(), isBaseCancelled ? 1 : 0);
+ Assert.assertEquals(blockingStatesForBaseEntitlement.size(), isBaseCancelled ? 2 : 1);
if (isBaseCancelled) {
- Assert.assertEquals(blockingStatesForBaseEntitlement.get(0).getBlockedId(), baseEntitlement.getId());
- Assert.assertEquals(blockingStatesForBaseEntitlement.get(0).getEffectiveDate().toLocalDate(), effectiveBaseCancellationDate);
- Assert.assertEquals(blockingStatesForBaseEntitlement.get(0).getType(), BlockingStateType.SUBSCRIPTION);
- Assert.assertEquals(blockingStatesForBaseEntitlement.get(0).getService(), EntitlementService.ENTITLEMENT_SERVICE_NAME);
- Assert.assertEquals(blockingStatesForBaseEntitlement.get(0).getStateName(), DefaultEntitlementApi.ENT_STATE_CANCELLED);
+ Assert.assertEquals(blockingStatesForBaseEntitlement.get(1).getBlockedId(), baseEntitlement.getId());
+ Assert.assertEquals(blockingStatesForBaseEntitlement.get(1).getEffectiveDate().toLocalDate(), effectiveBaseCancellationDate);
+ Assert.assertEquals(blockingStatesForBaseEntitlement.get(1).getType(), BlockingStateType.SUBSCRIPTION);
+ Assert.assertEquals(blockingStatesForBaseEntitlement.get(1).getService(), EntitlementService.ENTITLEMENT_SERVICE_NAME);
+ Assert.assertEquals(blockingStatesForBaseEntitlement.get(1).getStateName(), DefaultEntitlementApi.ENT_STATE_CANCELLED);
}
final List<BlockingState> blockingStatesForAddOn = blockingStatesForBlockedId(addOnEntitlement.getId());
- Assert.assertEquals(blockingStatesForAddOn.size(), 1);
- Assert.assertEquals(blockingStatesForAddOn.get(0).getBlockedId(), addOnEntitlement.getId());
- Assert.assertEquals(blockingStatesForAddOn.get(0).getEffectiveDate().toLocalDate(), effectiveAddOnCancellationDate);
- Assert.assertEquals(blockingStatesForAddOn.get(0).getType(), BlockingStateType.SUBSCRIPTION);
- Assert.assertEquals(blockingStatesForAddOn.get(0).getService(), EntitlementService.ENTITLEMENT_SERVICE_NAME);
- Assert.assertEquals(blockingStatesForAddOn.get(0).getStateName(), DefaultEntitlementApi.ENT_STATE_CANCELLED);
+ Assert.assertEquals(blockingStatesForAddOn.size(), 2);
+ Assert.assertEquals(blockingStatesForAddOn.get(1).getBlockedId(), addOnEntitlement.getId());
+ Assert.assertEquals(blockingStatesForAddOn.get(1).getEffectiveDate().toLocalDate(), effectiveAddOnCancellationDate);
+ Assert.assertEquals(blockingStatesForAddOn.get(1).getType(), BlockingStateType.SUBSCRIPTION);
+ Assert.assertEquals(blockingStatesForAddOn.get(1).getService(), EntitlementService.ENTITLEMENT_SERVICE_NAME);
+ Assert.assertEquals(blockingStatesForAddOn.get(1).getStateName(), DefaultEntitlementApi.ENT_STATE_CANCELLED);
}
private Collection<BlockingState> computeFutureBlockingStatesForAssociatedAddonsViaEntitlement(final DefaultEntitlement baseEntitlement) throws EntitlementApiException {
@@ -450,8 +446,9 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
}
private Collection<BlockingState> computeFutureBlockingStatesForAssociatedAddonsViaAccount(final DefaultEntitlement baseEntitlement) throws EntitlementApiException {
- final InternalTenantContext context = internalCallContextFactory.createInternalTenantContext(baseEntitlement.getAccountId(), callContext);
- final EventsStream eventsStream = Iterables.<EventsStream>find(Iterables.<EventsStream>concat(eventsStreamBuilder.buildForAccount(context).getEventsStreams().values()),
+ final AccountEventsStreams accountEventsStreams = eventsStreamBuilder.buildForAccount(internalCallContext);
+
+ final EventsStream eventsStream = Iterables.<EventsStream>find(Iterables.<EventsStream>concat(accountEventsStreams.getEventsStreams().values()),
new Predicate<EventsStream>() {
@Override
public boolean apply(final EventsStream input) {
@@ -467,8 +464,9 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
}
private Collection<BlockingState> computeBlockingStatesForAssociatedAddonsViaAccount(final DefaultEntitlement baseEntitlement, final DateTime effectiveDate) throws EntitlementApiException {
- final InternalTenantContext context = internalCallContextFactory.createInternalTenantContext(baseEntitlement.getAccountId(), callContext);
- final EventsStream eventsStream = Iterables.<EventsStream>find(Iterables.<EventsStream>concat(eventsStreamBuilder.buildForAccount(context).getEventsStreams().values()),
+ final AccountEventsStreams accountEventsStreams = eventsStreamBuilder.buildForAccount(internalCallContext);
+
+ final EventsStream eventsStream = Iterables.<EventsStream>find(Iterables.<EventsStream>concat(accountEventsStreams.getEventsStreams().values()),
new Predicate<EventsStream>() {
@Override
public boolean apply(final EventsStream input) {
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteNoDB.java b/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteNoDB.java
index b4f0792..df95234 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteNoDB.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteNoDB.java
@@ -20,6 +20,8 @@ package org.killbill.billing.entitlement;
import org.killbill.billing.GuicyKillbillTestSuiteNoDB;
import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.account.api.ImmutableAccountInternalApi;
import org.killbill.billing.catalog.api.CatalogService;
import org.killbill.billing.entitlement.block.BlockingChecker;
import org.killbill.billing.entitlement.dao.BlockingStateDao;
@@ -27,6 +29,7 @@ import org.killbill.billing.entitlement.glue.TestEntitlementModuleNoDB;
import org.killbill.billing.junction.BlockingInternalApi;
import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
import org.killbill.billing.tag.TagInternalApi;
+import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.tag.dao.TagDao;
import org.killbill.bus.api.PersistentBus;
import org.testng.annotations.AfterMethod;
@@ -40,8 +43,12 @@ import com.google.inject.Injector;
public abstract class EntitlementTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@Inject
+ protected AccountUserApi accountUserApi;
+ @Inject
protected AccountInternalApi accountInternalApi;
@Inject
+ protected ImmutableAccountInternalApi immutableAccountInternalApi;
+ @Inject
protected BlockingInternalApi blockingInternalApi;
@Inject
protected BlockingStateDao blockingStateDao;
@@ -57,6 +64,8 @@ public abstract class EntitlementTestSuiteNoDB extends GuicyKillbillTestSuiteNoD
protected TagInternalApi tagInternalApi;
@Inject
protected BlockingChecker blockingChecker;
+ @Inject
+ protected NonEntityDao nonEntityDao;
@BeforeClass(groups = "fast")
protected void beforeClass() throws Exception {
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java b/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
index 1fe5700..3ee58a2 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -21,17 +21,16 @@ package org.killbill.billing.entitlement;
import java.util.UUID;
import org.apache.shiro.SecurityUtils;
-import org.apache.shiro.authc.AuthenticationToken;
-import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.Ini;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
-import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.apache.shiro.util.ThreadContext;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.killbill.billing.GuicyKillbillTestSuiteWithEmbeddedDB;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountApiException;
import org.killbill.billing.account.api.AccountData;
import org.killbill.billing.account.api.AccountInternalApi;
import org.killbill.billing.account.api.AccountUserApi;
@@ -58,6 +57,7 @@ import org.killbill.billing.subscription.engine.core.DefaultSubscriptionBaseServ
import org.killbill.billing.tag.TagInternalApi;
import org.killbill.billing.util.api.AuditUserApi;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.tag.dao.TagDao;
import org.killbill.bus.api.PersistentBus;
import org.killbill.clock.ClockMock;
@@ -118,6 +118,8 @@ public class EntitlementTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWi
protected InternalCallContextFactory internalCallContextFactory;
@Inject
protected SecurityApi securityApi;
+ @Inject
+ protected NonEntityDao nonEntityDao;
protected Catalog catalog;
@@ -279,6 +281,14 @@ public class EntitlementTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWi
.build();
}
+ protected Account createAccount(final AccountData accountData) throws AccountApiException {
+ final Account account = accountApi.createAccount(accountData, callContext);
+
+ refreshCallContext(account.getId());
+
+ return account;
+ }
+
protected void assertListenerStatus() {
testListener.assertListenerStatus();
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java b/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java
index 8a5933b..dd29cd1 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java
@@ -127,6 +127,7 @@ public class InvoiceApiHelper {
@Nullable final BigDecimal positiveAdjAmount,
@Nullable final Currency currency,
final LocalDate effectiveDate,
+ final String description,
final InternalCallContext context) throws InvoiceApiException {
final InvoiceItem invoiceItemToBeAdjusted = Iterables.<InvoiceItem>tryFind(invoiceToBeAdjusted.getInvoiceItems(),
new Predicate<InvoiceItem>() {
@@ -159,7 +160,7 @@ public class InvoiceApiHelper {
invoiceItemToBeAdjusted.getInvoiceId(),
invoiceItemToBeAdjusted.getAccountId(),
effectiveDate,
- null,
+ description,
amountToAdjust,
currencyForAdjustment,
invoiceItemToBeAdjusted.getId());
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
index fcdd838..7c69cd5 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
@@ -20,7 +20,6 @@ package org.killbill.billing.invoice.api.svcs;
import java.math.BigDecimal;
import java.util.Collection;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -112,11 +111,6 @@ public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
}
@Override
- public InvoicePayment getInvoicePaymentForRefund(final UUID paymentId, final InternalTenantContext context) throws InvoiceApiException {
- return getInvoicePayment(paymentId, InvoicePaymentType.REFUND, context);
- }
-
- @Override
public InvoicePayment getInvoicePaymentForChargeback(final UUID paymentId, final InternalTenantContext context) throws InvoiceApiException {
return getInvoicePayment(paymentId, InvoicePaymentType.CHARGED_BACK, context);
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
index 92f6b7e..9499079 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -28,18 +28,18 @@ import java.util.UUID;
import javax.annotation.Nullable;
-import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.killbill.billing.ErrorCode;
import org.killbill.billing.ObjectType;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountApiException;
import org.killbill.billing.account.api.AccountInternalApi;
-import org.killbill.billing.account.api.ImmutableAccountData;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.invoice.InvoiceDispatcher;
+import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications;
+import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications.SubscriptionNotification;
import org.killbill.billing.invoice.api.DryRunArguments;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceApiException;
@@ -50,6 +50,7 @@ import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.invoice.api.InvoiceUserApi;
import org.killbill.billing.invoice.api.WithAccountLock;
import org.killbill.billing.invoice.dao.InvoiceDao;
+import org.killbill.billing.invoice.dao.InvoiceItemModelDao;
import org.killbill.billing.invoice.dao.InvoiceModelDao;
import org.killbill.billing.invoice.model.CreditAdjInvoiceItem;
import org.killbill.billing.invoice.model.DefaultInvoice;
@@ -77,6 +78,7 @@ import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.inject.Inject;
@@ -115,8 +117,11 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
}
@Override
- public List<Invoice> getInvoicesByAccount(final UUID accountId, final TenantContext context) {
- final List<InvoiceModelDao> invoicesByAccount = dao.getInvoicesByAccount(internalCallContextFactory.createInternalTenantContext(accountId, context));
+ public List<Invoice> getInvoicesByAccount(final UUID accountId, boolean includesMigrated, final TenantContext context) {
+ final List<InvoiceModelDao> invoicesByAccount = includesMigrated ?
+ dao.getAllInvoicesByAccount(internalCallContextFactory.createInternalTenantContext(accountId, context)) :
+ dao.getInvoicesByAccount(internalCallContextFactory.createInternalTenantContext(accountId, context));
+
return fromInvoiceModelDao(invoicesByAccount);
}
@@ -128,7 +133,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
@Override
public Invoice getInvoiceByPayment(final UUID paymentId, final TenantContext context) throws InvoiceApiException {
- final InternalTenantContext tenantContext = internalCallContextFactory.createInternalTenantContext(context);
+ final InternalTenantContext tenantContext = internalCallContextFactory.createInternalTenantContext(paymentId, ObjectType.PAYMENT, context);
final UUID invoiceId = dao.getInvoiceIdByPaymentId(paymentId, tenantContext);
if (invoiceId == null) {
throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, paymentId);
@@ -144,7 +149,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
@Override
public Pagination<InvoiceModelDao> build() {
// Invoices will be shallow, i.e. won't contain items nor payments
- return dao.get(offset, limit, internalCallContextFactory.createInternalTenantContext(context));
+ return dao.get(offset, limit, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
}
},
new Function<InvoiceModelDao, Invoice>() {
@@ -163,7 +168,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
@Override
public Pagination<InvoiceModelDao> build() {
// Invoices will be shallow, i.e. won't contain items nor payments
- return dao.searchInvoices(searchKey, offset, limit, internalCallContextFactory.createInternalTenantContext(context));
+ return dao.searchInvoices(searchKey, offset, limit, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
}
},
new Function<InvoiceModelDao, Invoice>() {
@@ -195,7 +200,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
@Override
public Invoice getInvoiceByNumber(final Integer number, final TenantContext context) throws InvoiceApiException {
// The account record id will be populated in the DAO
- return new DefaultInvoice(dao.getByNumber(number, internalCallContextFactory.createInternalTenantContext(context)));
+ return new DefaultInvoice(dao.getByNumber(number, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context)));
}
@Override
@@ -209,15 +214,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
final CallContext context) throws InvoiceApiException {
final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(accountId, context);
- final ImmutableAccountData account;
- try {
- account = accountUserApi.getImmutableAccountDataById(accountId, internalContext);
- } catch (final AccountApiException e) {
- throw new InvoiceApiException(e, ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID, e.toString());
- }
-
- final DateTime processingDateTime = targetDate != null ? targetDate.toDateTimeAtCurrentTime(account.getTimeZone()) : null;
- final Invoice result = dispatcher.processAccount(accountId, processingDateTime, dryRunArguments, internalContext);
+ final Invoice result = dispatcher.processAccount(accountId, targetDate, dryRunArguments, internalContext);
if (result == null) {
throw new InvoiceApiException(ErrorCode.INVOICE_NOTHING_TO_DO, accountId, targetDate != null ? targetDate : "null");
} else {
@@ -324,23 +321,23 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
}
return new CreditAdjInvoiceItem(creditItem.getId(), creditItem.getCreatedDate(), creditItem.getInvoiceId(), creditItem.getAccountId(),
- creditItem.getStartDate(), creditItem.getAmount().negate(), creditItem.getCurrency());
+ creditItem.getStartDate(), creditItem.getDescription(), creditItem.getAmount().negate(), creditItem.getCurrency());
}
@Override
public InvoiceItem insertCredit(final UUID accountId, final BigDecimal amount, final LocalDate effectiveDate,
- final Currency currency, final boolean autoCommit, final CallContext context) throws InvoiceApiException {
- return insertCreditForInvoice(accountId, null, amount, effectiveDate, currency, autoCommit, context);
+ final Currency currency, final boolean autoCommit, final String description, final CallContext context) throws InvoiceApiException {
+ return insertCreditForInvoice(accountId, null, amount, effectiveDate, currency, autoCommit, description, context);
}
@Override
public InvoiceItem insertCreditForInvoice(final UUID accountId, final UUID invoiceId, final BigDecimal amount,
- final LocalDate effectiveDate, final Currency currency, final CallContext context) throws InvoiceApiException {
- return insertCreditForInvoice(accountId, invoiceId, amount, effectiveDate, currency, false, context);
+ final LocalDate effectiveDate, final Currency currency, final String description, final CallContext context) throws InvoiceApiException {
+ return insertCreditForInvoice(accountId, invoiceId, amount, effectiveDate, currency, false, description, context);
}
private InvoiceItem insertCreditForInvoice(final UUID accountId, final UUID invoiceId, final BigDecimal amount, final LocalDate effectiveDate,
- final Currency currency, final boolean autoCommit, final CallContext context) throws InvoiceApiException {
+ final Currency currency, final boolean autoCommit, final String description, final CallContext context) throws InvoiceApiException {
if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new InvoiceApiException(ErrorCode.CREDIT_AMOUNT_INVALID, amount);
}
@@ -369,6 +366,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
invoiceForCredit.getId(),
accountId,
effectiveDate,
+ description,
// Note! The amount is negated here!
amount.negate(),
currency);
@@ -392,14 +390,14 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
@Override
public InvoiceItem insertInvoiceItemAdjustment(final UUID accountId, final UUID invoiceId, final UUID invoiceItemId,
- final LocalDate effectiveDate, final CallContext context) throws InvoiceApiException {
- return insertInvoiceItemAdjustment(accountId, invoiceId, invoiceItemId, effectiveDate, null, null, context);
+ final LocalDate effectiveDate, final String description, final CallContext context) throws InvoiceApiException {
+ return insertInvoiceItemAdjustment(accountId, invoiceId, invoiceItemId, effectiveDate, null, null, description, context);
}
@Override
public InvoiceItem insertInvoiceItemAdjustment(final UUID accountId, final UUID invoiceId, final UUID invoiceItemId,
final LocalDate effectiveDate, @Nullable final BigDecimal amount,
- @Nullable final Currency currency, final CallContext context) throws InvoiceApiException {
+ @Nullable final Currency currency, final String description, final CallContext context) throws InvoiceApiException {
if (amount != null && amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_ADJUSTMENT_AMOUNT_SHOULD_BE_POSITIVE, amount);
}
@@ -413,6 +411,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
amount,
currency,
effectiveDate,
+ description,
internalCallContextFactory.createInternalCallContext(accountId, context));
invoice.addInvoiceItem(adjustmentItem);
@@ -466,6 +465,40 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
dao.consumeExstingCBAOnAccountWithUnpaidInvoices(accountId, internalCallContextFactory.createInternalCallContext(accountId, context));
}
+ @Override
+ public UUID createMigrationInvoice(final UUID accountId, final LocalDate targetDate, final Iterable<InvoiceItem> items, final CallContext context) {
+ final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(accountId, context);
+ final LocalDate createdDate = internalCallContext.toLocalDate(internalCallContext.getCreatedDate());
+ final InvoiceModelDao migrationInvoice = new InvoiceModelDao(accountId, createdDate, targetDate, items.iterator().next().getCurrency(), true);
+
+
+ final List<InvoiceItemModelDao> itemModelDaos = ImmutableList.copyOf(Iterables.transform(items, new Function<InvoiceItem, InvoiceItemModelDao>() {
+ @Override
+ public InvoiceItemModelDao apply(final InvoiceItem input) {
+ return new InvoiceItemModelDao(internalCallContext.getCreatedDate(),
+ input.getInvoiceItemType(),
+ migrationInvoice.getId(),
+ accountId,
+ input.getBundleId(),
+ input.getSubscriptionId(),
+ input.getDescription(),
+ input.getPlanName(),
+ input.getPhaseName(),
+ input.getUsageName(),
+ input.getStartDate(),
+ input.getEndDate(),
+ input.getAmount(),
+ input.getRate(),
+ input.getCurrency(),
+ input.getLinkedItemId());
+
+ }
+ }));
+
+ dao.createInvoice(migrationInvoice, itemModelDaos, true, new FutureAccountNotifications(ImmutableMap.<UUID, List<SubscriptionNotification>>of()), internalCallContext);
+ return migrationInvoice.getId();
+ }
+
private void notifyBusOfInvoiceAdjustment(final UUID invoiceId, final UUID accountId, final InternalCallContext context) {
try {
eventBus.post(new DefaultInvoiceAdjustmentEvent(invoiceId, accountId, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()));
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/calculator/InvoiceCalculatorUtils.java b/invoice/src/main/java/org/killbill/billing/invoice/calculator/InvoiceCalculatorUtils.java
index 23b11ef..17c6b9b 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/calculator/InvoiceCalculatorUtils.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/calculator/InvoiceCalculatorUtils.java
@@ -21,7 +21,6 @@ import java.math.BigDecimal;
import javax.annotation.Nullable;
import org.joda.time.DateTime;
-
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoiceItemType;
@@ -72,16 +71,20 @@ public abstract class InvoiceCalculatorUtils {
public static BigDecimal computeInvoiceBalance(final Currency currency,
@Nullable final Iterable<InvoiceItem> invoiceItems,
- @Nullable final Iterable<InvoicePayment> invoicePayments) {
- final BigDecimal invoiceBalance = computeInvoiceAmountCharged(currency, invoiceItems)
+ @Nullable final Iterable<InvoicePayment> invoicePayments,
+ boolean writtenOffOrMigrated) {
+ if (writtenOffOrMigrated) {
+ return BigDecimal.ZERO;
+ }
+
+ final BigDecimal amountPaid = computeInvoiceAmountPaid(currency, invoicePayments)
+ .add(computeInvoiceAmountRefunded(currency, invoicePayments));
+
+ final BigDecimal chargedAmount = computeInvoiceAmountCharged(currency, invoiceItems)
.add(computeInvoiceAmountCredited(currency, invoiceItems))
- .add(computeInvoiceAmountAdjustedForAccountCredit(currency, invoiceItems))
- .add(
- computeInvoiceAmountPaid(currency, invoicePayments).negate()
- .add(
- computeInvoiceAmountRefunded(currency, invoicePayments).negate()
- )
- );
+ .add(computeInvoiceAmountAdjustedForAccountCredit(currency, invoiceItems));
+
+ final BigDecimal invoiceBalance = chargedAmount.add(amountPaid.negate());
return KillBillMoney.of(invoiceBalance, currency);
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
index 3e3c4af..a322b95 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -51,7 +51,6 @@ import org.killbill.billing.invoice.api.user.DefaultInvoiceCreationEvent;
import org.killbill.billing.invoice.model.DefaultInvoice;
import org.killbill.billing.invoice.notification.NextBillingDatePoster;
import org.killbill.billing.invoice.notification.ParentInvoiceCommitmentPoster;
-import org.killbill.billing.util.AccountDateAndTimeZoneContext;
import org.killbill.billing.util.UUIDs;
import org.killbill.billing.util.cache.Cachable.CacheType;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
@@ -64,7 +63,6 @@ import org.killbill.billing.util.entity.dao.EntityDaoBase;
import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
-import org.killbill.billing.util.timezone.DefaultAccountDateAndTimeZoneContext;
import org.killbill.bus.api.BusEvent;
import org.killbill.bus.api.PersistentBus;
import org.killbill.bus.api.PersistentBus.EventBusException;
@@ -123,7 +121,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
final CBADao cbaDao,
final ParentInvoiceCommitmentPoster parentInvoiceCommitmentPoster,
final InternalCallContextFactory internalCallContextFactory) {
- super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao), InvoiceSqlDao.class);
+ super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory), InvoiceSqlDao.class);
this.nextBillingDatePoster = nextBillingDatePoster;
this.eventBus = eventBus;
this.invoiceConfig = invoiceConfig;
@@ -146,9 +144,9 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceModelDao>>() {
@Override
public List<InvoiceModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
- final InvoiceSqlDao invoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+ final InvoiceSqlDao invoiceSqlDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
- final List<InvoiceModelDao> invoices = ImmutableList.<InvoiceModelDao>copyOf(INVOICE_MODEL_DAO_ORDERING.sortedCopy(Iterables.<InvoiceModelDao>filter(invoiceDao.getByAccountRecordId(context),
+ final List<InvoiceModelDao> invoices = ImmutableList.<InvoiceModelDao>copyOf(INVOICE_MODEL_DAO_ORDERING.sortedCopy(Iterables.<InvoiceModelDao>filter(invoiceSqlDao.getByAccountRecordId(context),
new Predicate<InvoiceModelDao>() {
@Override
public boolean apply(final InvoiceModelDao invoice) {
@@ -201,9 +199,9 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<InvoiceModelDao>() {
@Override
public InvoiceModelDao inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
- final InvoiceSqlDao invoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+ final InvoiceSqlDao invoiceSqlDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
- final InvoiceModelDao invoice = invoiceDao.getById(invoiceId.toString(), context);
+ final InvoiceModelDao invoice = invoiceSqlDao.getById(invoiceId.toString(), context);
if (invoice == null) {
throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceId);
}
@@ -565,7 +563,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
}
// We expect the code to correctly pass the account currency -- the payment code, more generic accept chargeBack in different currencies,
// but this is only for direct payment (no invoice)
- Preconditions.checkArgument(invoicePayment.getCurrency() == currency);
+ Preconditions.checkArgument(invoicePayment.getCurrency() == currency, String.format("Invoice payment currency %s doesn't match chargeback currency %s", invoicePayment.getCurrency(), currency));
final UUID invoicePaymentId = invoicePayment.getId();
final BigDecimal maxChargedBackAmount = invoiceDaoHelper.getRemainingAmountPaidFromTransaction(invoicePaymentId, entitySqlDaoWrapperFactory, context);
@@ -856,10 +854,10 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
if (isInvoiceNotificationEnabled) {
final DateTime curDryRunNotificationTime = cur.getEffectiveDate().minus(dryRunNotificationTime);
final DateTime effectiveCurDryRunNotificationTime = (curDryRunNotificationTime.isAfter(clock.getUTCNow())) ? curDryRunNotificationTime : clock.getUTCNow();
- nextBillingDatePoster.insertNextBillingDryRunNotificationFromTransaction(entitySqlDaoWrapperFactory, accountId, subscriptionId, effectiveCurDryRunNotificationTime, cur.getEffectiveDate(), callbackDateTimePerSubscriptions.getAccountDateAndTimeZoneContext(), internalCallContext);
+ nextBillingDatePoster.insertNextBillingDryRunNotificationFromTransaction(entitySqlDaoWrapperFactory, accountId, subscriptionId, effectiveCurDryRunNotificationTime, cur.getEffectiveDate(), internalCallContext);
}
if (cur.isForInvoiceNotificationTrigger()) {
- nextBillingDatePoster.insertNextBillingNotificationFromTransaction(entitySqlDaoWrapperFactory, accountId, subscriptionId, cur.getEffectiveDate(), callbackDateTimePerSubscriptions.getAccountDateAndTimeZoneContext(), internalCallContext);
+ nextBillingDatePoster.insertNextBillingNotificationFromTransaction(entitySqlDaoWrapperFactory, accountId, subscriptionId, cur.getEffectiveDate(), internalCallContext);
}
}
}
@@ -982,13 +980,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
private void notifyOfParentInvoiceCreation(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InvoiceModelDao parentInvoice,
final FutureAccountNotifications callbackDateTime, final InternalCallContext context) {
DateTime futureNotificationDate = parentInvoice.getCreatedDate().withTimeAtStartOfDay().plusDays(1);
- AccountDateAndTimeZoneContext accountDateAndTimeZone;
- if ((callbackDateTime != null) && (callbackDateTime.getAccountDateAndTimeZoneContext() != null)) {
- accountDateAndTimeZone = callbackDateTime.getAccountDateAndTimeZoneContext();
- } else {
- accountDateAndTimeZone = new DefaultAccountDateAndTimeZoneContext(futureNotificationDate, DateTimeZone.UTC);
- }
- parentInvoiceCommitmentPoster.insertParentInvoiceFromTransactionInternal(entitySqlDaoWrapperFactory, parentInvoice.getId(), futureNotificationDate, accountDateAndTimeZone, context);
+ parentInvoiceCommitmentPoster.insertParentInvoiceFromTransactionInternal(entitySqlDaoWrapperFactory, parentInvoice.getId(), futureNotificationDate, context);
}
@Override
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
index 1e4bf33..0352a9b 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
@@ -182,7 +182,7 @@ public class InvoiceDaoHelper {
public boolean apply(final InvoiceModelDao in) {
final BigDecimal balance = InvoiceModelDaoHelper.getBalance(in);
log.debug("Computed balance={} for invoice={}", balance, in);
- return InvoiceStatus.COMMITTED.equals(in.getStatus()) && !in.isWrittenOff() && (balance.compareTo(BigDecimal.ZERO) >= 1) && (upToDate == null || !in.getTargetDate().isAfter(upToDate));
+ return InvoiceStatus.COMMITTED.equals(in.getStatus()) && (balance.compareTo(BigDecimal.ZERO) >= 1) && (upToDate == null || !in.getTargetDate().isAfter(upToDate));
}
});
return new ArrayList<InvoiceModelDao>(unpaidInvoices);
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemModelDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemModelDao.java
index 8754a96..8805d52 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemModelDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemModelDao.java
@@ -87,6 +87,14 @@ public class InvoiceItemModelDao extends EntityModelDaoBase implements EntityMod
invoiceItem.getAmount(), invoiceItem.getRate(), invoiceItem.getCurrency(), invoiceItem.getLinkedItemId());
}
+ /*
+ public InvoiceItemModelDao(final InvoiceItem invoiceItem, final UUID invoiceId) {
+ this(invoiceItem.getId(), invoiceItem.getCreatedDate(), invoiceItem.getInvoiceItemType(), invoiceId, invoiceItem.getAccountId(), invoiceItem.getBundleId(),
+ invoiceItem.getSubscriptionId(), invoiceItem.getDescription(), invoiceItem.getPlanName(), invoiceItem.getPhaseName(), invoiceItem.getUsageName(),
+ invoiceItem.getStartDate(), invoiceItem.getEndDate(),
+ invoiceItem.getAmount(), invoiceItem.getRate(), invoiceItem.getCurrency(), invoiceItem.getLinkedItemId());
+ }
+*/
public InvoiceItemType getType() {
return type;
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDaoHelper.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDaoHelper.java
index ebb19d2..72c3008 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDaoHelper.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDaoHelper.java
@@ -47,7 +47,8 @@ public class InvoiceModelDaoHelper {
public InvoicePayment apply(final InvoicePaymentModelDao input) {
return new DefaultInvoicePayment(input);
}
- }));
+ }),
+ invoiceModelDao.isMigrated() || invoiceModelDao.isWrittenOff());
}
public static BigDecimal getCBAAmount(final InvoiceModelDao invoiceModelDao) {
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 c31efad..638c5d8 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
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -75,7 +75,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
validateTargetDate(targetDate);
final LocalDate adjustedTargetDate = adjustTargetDate(existingInvoices, targetDate);
- final LocalDate invoiceDate = events.getAccountDateAndTimeZoneContext().computeLocalDateFromFixedAccountOffset(clock.getUTCNow());
+ final LocalDate invoiceDate = context.toLocalDate(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 f6726f6..e77696e 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
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -33,6 +33,7 @@ import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.catalog.api.BillingMode;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.PhaseType;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceItem;
@@ -45,7 +46,6 @@ 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;
@@ -84,17 +84,17 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
// Generate list of proposed invoice items based on billing events from junction-- proposed items are ALL items since beginning of time
final List<InvoiceItem> proposedItems = new ArrayList<InvoiceItem>();
- processRecurringBillingEvents(invoiceId, account.getId(), eventSet, targetDate, targetCurrency, proposedItems, perSubscriptionFutureNotificationDate);
- processFixedBillingEvents(invoiceId, account.getId(), eventSet, targetDate, targetCurrency, proposedItems);
-
+ processRecurringBillingEvents(invoiceId, account.getId(), eventSet, targetDate, targetCurrency, proposedItems, perSubscriptionFutureNotificationDate, existingInvoices, internalCallContext);
+ processFixedBillingEvents(invoiceId, account.getId(), eventSet, targetDate, targetCurrency, proposedItems, internalCallContext);
accountItemTree.mergeWithProposedItems(proposedItems);
return accountItemTree.getResultingItemList();
}
private void processRecurringBillingEvents(final UUID invoiceId, final UUID accountId, final BillingEventSet events,
- final LocalDate targetDate, final Currency currency, final List<InvoiceItem> proposedItems,
- final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDate) throws InvoiceApiException {
-
+ final LocalDate targetDate, final Currency currency, final List<InvoiceItem> proposedItems,
+ final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDate,
+ @Nullable final List<Invoice> existingInvoices,
+ final InternalCallContext internalCallContext) throws InvoiceApiException {
if (events.size() == 0) {
return;
}
@@ -113,11 +113,12 @@ 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, events.getAccountDateAndTimeZoneContext());
+ final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, thisEvent, adjustedNextEvent, targetDate, currency, logStringBuilder, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate, internalCallContext);
proposedItems.addAll(newProposedItems);
}
}
- final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, nextEvent, null, targetDate, currency, logStringBuilder, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate, events.getAccountDateAndTimeZoneContext());
+ final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, nextEvent, null, targetDate, currency, logStringBuilder, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate, internalCallContext);
+
proposedItems.addAll(newProposedItems);
log.info(logStringBuilder.toString());
@@ -126,18 +127,16 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
}
@VisibleForTesting
- void processFixedBillingEvents(final UUID invoiceId, final UUID accountId, final BillingEventSet events, final LocalDate targetDate, final Currency currency, final List<InvoiceItem> proposedItems) {
-
- final AccountDateAndTimeZoneContext dateAndTimeZoneContext = events.getAccountDateAndTimeZoneContext();
-
+ void processFixedBillingEvents(final UUID invoiceId, final UUID accountId, final BillingEventSet events, final LocalDate targetDate,
+ final Currency currency, final List<InvoiceItem> proposedItems, final InternalCallContext internalCallContext) {
InvoiceItem prevItem = null;
final Iterator<BillingEvent> eventIt = events.iterator();
while (eventIt.hasNext()) {
final BillingEvent thisEvent = eventIt.next();
- final InvoiceItem currentFixedPriceItem = generateFixedPriceItem(invoiceId, accountId, thisEvent, targetDate, currency, dateAndTimeZoneContext);
- if (!isSameDayAndSameSubscription(prevItem, thisEvent, dateAndTimeZoneContext) && prevItem != null) {
+ final InvoiceItem currentFixedPriceItem = generateFixedPriceItem(invoiceId, accountId, thisEvent, targetDate, currency, internalCallContext);
+ if (!isSameDayAndSameSubscription(prevItem, thisEvent, internalCallContext) && prevItem != null) {
proposedItems.add(prevItem);
}
prevItem = currentFixedPriceItem;
@@ -149,8 +148,8 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
}
@VisibleForTesting
- boolean isSameDayAndSameSubscription(final InvoiceItem prevComputedFixedItem, final BillingEvent currentBillingEvent, final AccountDateAndTimeZoneContext dateAndTimeZoneContext) {
- final LocalDate curLocalEffectiveDate = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(currentBillingEvent.getEffectiveDate());
+ boolean isSameDayAndSameSubscription(final InvoiceItem prevComputedFixedItem, final BillingEvent currentBillingEvent, final InternalCallContext internalCallContext) {
+ final LocalDate curLocalEffectiveDate = internalCallContext.toLocalDate(currentBillingEvent.getEffectiveDate());
if (prevComputedFixedItem != null && /* If we have computed a previous item */
prevComputedFixedItem.getStartDate().compareTo(curLocalEffectiveDate) == 0 && /* The current billing event happens at the same date */
prevComputedFixedItem.getSubscriptionId().compareTo(currentBillingEvent.getSubscription().getId()) == 0 /* The current billing event happens for the same subscription */) {
@@ -160,22 +159,26 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
}
}
-
// Turn a set of events into a list of invoice items. Note that the dates on the invoice items will be rounded (granularity of a day)
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,
- final AccountDateAndTimeZoneContext dateAndTimeZoneContext) throws InvoiceApiException {
+ final InternalCallContext internalCallContext) throws InvoiceApiException {
final List<InvoiceItem> items = new ArrayList<InvoiceItem>();
+ // For FIXEDTERM phases we need to stop when the specified duration has been reached
+ final LocalDate maxEndDate = thisEvent.getPlanPhase().getPhaseType() == PhaseType.FIXEDTERM ?
+ thisEvent.getPlanPhase().getDuration().addToLocalDate(internalCallContext.toLocalDate(thisEvent.getEffectiveDate())) :
+ null;
+
// Handle recurring items
final BillingPeriod billingPeriod = thisEvent.getBillingPeriod();
if (billingPeriod != BillingPeriod.NO_BILLING_PERIOD) {
- final LocalDate startDate = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(thisEvent.getEffectiveDate());
+ final LocalDate startDate = internalCallContext.toLocalDate(thisEvent.getEffectiveDate());
if (!startDate.isAfter(targetDate)) {
- final LocalDate endDate = (nextEvent == null) ? null : dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(nextEvent.getEffectiveDate());
+ final LocalDate endDate = (nextEvent == null) ? null : internalCallContext.toLocalDate(nextEvent.getEffectiveDate());
final int billCycleDayLocal = thisEvent.getBillCycleDayLocal();
@@ -187,8 +190,12 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
}
for (final RecurringInvoiceItemData itemDatum : itemDataWithNextBillingCycleDate.getItemData()) {
- final BigDecimal rate = thisEvent.getRecurringPrice();
+ // Stop if there a maxEndDate and we have reached it
+ if (maxEndDate != null && maxEndDate.compareTo(itemDatum.getEndDate()) < 0) {
+ break;
+ }
+ final BigDecimal rate = thisEvent.getRecurringPrice();
if (rate != null) {
final BigDecimal amount = KillBillMoney.of(itemDatum.getNumberOfCycles().multiply(rate), currency);
final RecurringInvoiceItem recurringItem = new RecurringInvoiceItem(invoiceId,
@@ -197,7 +204,8 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
thisEvent.getSubscription().getId(),
thisEvent.getPlan().getName(),
thisEvent.getPlanPhase().getName(),
- itemDatum.getStartDate(), itemDatum.getEndDate(),
+ itemDatum.getStartDate(),
+ itemDatum.getEndDate(),
amount, rate, currency);
items.add(recurringItem);
}
@@ -342,8 +350,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 AccountDateAndTimeZoneContext dateAndTimeZoneContext) {
- final LocalDate roundedStartDate = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(thisEvent.getEffectiveDate());
+ final LocalDate targetDate, final Currency currency, final InternalCallContext internalCallContext) {
+ final LocalDate roundedStartDate = internalCallContext.toLocalDate(thisEvent.getEffectiveDate());
if (roundedStartDate.isAfter(targetDate)) {
return null;
} else {
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceDateUtils.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceDateUtils.java
index f4f4a38..879ef43 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceDateUtils.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceDateUtils.java
@@ -147,24 +147,6 @@ public class InvoiceDateUtils {
return calculateProrationBetweenDates(previousBillThroughDate, endDate, previousBillThroughDate, nextBillThroughDate);
}
- /*
- public static LocalDate calculateBillingCycleDateOnOrAfter(final LocalDate date, final DateTimeZone accountTimeZone,
- final int billingCycleDayLocal) {
- final DateTime tmp = date.toDateTimeAtStartOfDay(accountTimeZone);
- final DateTime proposedDateTime = calculateBillingCycleDateOnOrAfter(tmp, billingCycleDayLocal);
-
- return new LocalDate(proposedDateTime, accountTimeZone);
- }
-
- public static LocalDate calculateBillingCycleDateAfter(final LocalDate date, final DateTimeZone accountTimeZone,
- final int billingCycleDayLocal) {
- final DateTime tmp = date.toDateTimeAtStartOfDay(accountTimeZone);
- final DateTime proposedDateTime = calculateBillingCycleDateAfter(tmp, billingCycleDayLocal);
-
- return new LocalDate(proposedDateTime, accountTimeZone);
- }
- */
-
public static LocalDate calculateBillingCycleDateOnOrAfter(final LocalDate date, final int billingCycleDayLocal) {
final int lastDayOfMonth = date.dayOfMonth().getMaximumValue();
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 5051892..d94b8f9 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
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -27,7 +27,6 @@ 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.account.api.ImmutableAccountData;
import org.killbill.billing.callcontext.InternalCallContext;
@@ -69,7 +68,6 @@ public class UsageInvoiceItemGenerator extends InvoiceItemGenerator {
this.rawUsageOptimizer = rawUsageOptimizer;
}
-
@Override
public List<InvoiceItem> generateItems(final ImmutableAccountData account,
final UUID invoiceId,
@@ -79,11 +77,10 @@ public class UsageInvoiceItemGenerator extends InvoiceItemGenerator {
final Currency targetCurrency,
final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates,
final InternalCallContext internalCallContext) throws InvoiceApiException {
-
final Map<UUID, List<InvoiceItem>> perSubscriptionConsumableInArrearUsageItems = extractPerSubscriptionExistingConsumableInArrearUsageItems(eventSet.getUsages(), existingInvoices);
try {
- final LocalDate minBillingEventDate = getMinBillingEventDate(eventSet, account.getTimeZone());
+ final LocalDate minBillingEventDate = getMinBillingEventDate(eventSet, internalCallContext);
final List<InvoiceItem> items = Lists.newArrayList();
final Iterator<BillingEvent> events = eventSet.iterator();
@@ -94,7 +91,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 = eventSet.getAccountDateAndTimeZoneContext().computeLocalDateFromFixedAccountOffset(event.getEffectiveDate());
+ final LocalDate eventLocalEffectiveDate = internalCallContext.toLocalDate(event.getEffectiveDate());
if (eventLocalEffectiveDate.isAfter(targetDate)) {
continue;
}
@@ -118,7 +115,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(), eventSet.getAccountDateAndTimeZoneContext());
+ final SubscriptionConsumableInArrear subscriptionConsumableInArrear = new SubscriptionConsumableInArrear(account.getId(), invoiceId, curEvents, rawUsageOptimizerResult.getRawUsage(), targetDate, rawUsageOptimizerResult.getRawUsageStartDate(), internalCallContext);
final List<InvoiceItem> consumableInUsageArrearItems = perSubscriptionConsumableInArrearUsageItems.get(curSubscriptionId);
final SubscriptionConsumableInArrearItemsAndNextNotificationDate subscriptionResult = subscriptionConsumableInArrear.computeMissingUsageInvoiceItems(consumableInUsageArrearItems != null ? consumableInUsageArrearItems : ImmutableList.<InvoiceItem>of());
@@ -131,7 +128,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(), eventSet.getAccountDateAndTimeZoneContext());
+ final SubscriptionConsumableInArrear subscriptionConsumableInArrear = new SubscriptionConsumableInArrear(account.getId(), invoiceId, curEvents, rawUsageOptimizerResult.getRawUsage(), targetDate, rawUsageOptimizerResult.getRawUsageStartDate(), internalCallContext);
final List<InvoiceItem> consumableInUsageArrearItems = perSubscriptionConsumableInArrearUsageItems.get(curSubscriptionId);
final SubscriptionConsumableInArrearItemsAndNextNotificationDate subscriptionResult = subscriptionConsumableInArrear.computeMissingUsageInvoiceItems(consumableInUsageArrearItems != null ? consumableInUsageArrearItems : ImmutableList.<InvoiceItem>of());
@@ -146,8 +143,7 @@ public class UsageInvoiceItemGenerator extends InvoiceItemGenerator {
}
}
-
- private LocalDate getMinBillingEventDate(final BillingEventSet eventSet, final DateTimeZone accountTimeZone) {
+ private LocalDate getMinBillingEventDate(final BillingEventSet eventSet, final InternalCallContext internalCallContext) {
DateTime minDate = null;
final Iterator<BillingEvent> events = eventSet.iterator();
while (events.hasNext()) {
@@ -156,7 +152,7 @@ public class UsageInvoiceItemGenerator extends InvoiceItemGenerator {
minDate = cur.getEffectiveDate();
}
}
- return eventSet.getAccountDateAndTimeZoneContext().computeLocalDateFromFixedAccountOffset(minDate);
+ return internalCallContext.toLocalDate(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/glue/DefaultInvoiceModule.java b/invoice/src/main/java/org/killbill/billing/invoice/glue/DefaultInvoiceModule.java
index 3080c5a..b59dd34 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/glue/DefaultInvoiceModule.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/glue/DefaultInvoiceModule.java
@@ -25,7 +25,6 @@ import org.killbill.billing.invoice.InvoiceTagHandler;
import org.killbill.billing.invoice.api.DefaultInvoiceService;
import org.killbill.billing.invoice.api.InvoiceApiHelper;
import org.killbill.billing.invoice.api.InvoiceInternalApi;
-import org.killbill.billing.invoice.api.InvoiceMigrationApi;
import org.killbill.billing.invoice.api.InvoiceNotifier;
import org.killbill.billing.invoice.api.InvoicePaymentApi;
import org.killbill.billing.invoice.api.InvoiceService;
@@ -33,7 +32,6 @@ import org.killbill.billing.invoice.api.InvoiceUserApi;
import org.killbill.billing.invoice.api.formatters.InvoiceFormatterFactory;
import org.killbill.billing.invoice.api.formatters.ResourceBundleFactory;
import org.killbill.billing.invoice.api.invoice.DefaultInvoicePaymentApi;
-import org.killbill.billing.invoice.api.migration.DefaultInvoiceMigrationApi;
import org.killbill.billing.invoice.api.svcs.DefaultInvoiceInternalApi;
import org.killbill.billing.invoice.api.user.DefaultInvoiceUserApi;
import org.killbill.billing.invoice.dao.CBADao;
@@ -104,10 +102,6 @@ public class DefaultInvoiceModule extends KillBillModule implements InvoiceModul
bind(ResourceBundleFactory.class).to(DefaultResourceBundleFactory.class).asEagerSingleton();
}
- @Override
- public void installInvoiceMigrationApi() {
- bind(InvoiceMigrationApi.class).to(DefaultInvoiceMigrationApi.class).asEagerSingleton();
- }
protected void installNotifiers() {
bind(NextBillingDateNotifier.class).to(DefaultNextBillingDateNotifier.class).asEagerSingleton();
@@ -163,7 +157,6 @@ public class DefaultInvoiceModule extends KillBillModule implements InvoiceModul
installInvoiceUserApi();
installInvoiceInternalApi();
installInvoicePaymentApi();
- installInvoiceMigrationApi();
installResourceBundleFactory();
bind(RawUsageOptimizer.class).asEagerSingleton();
bind(InvoiceApiHelper.class).asEagerSingleton();
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 36f4226..29e410c 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -87,13 +87,11 @@ 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.DefaultAccountDateAndTimeZoneContext;
import org.killbill.bus.api.PersistentBus;
import org.killbill.bus.api.PersistentBus.EventBusException;
import org.killbill.clock.Clock;
@@ -113,7 +111,9 @@ import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import com.google.inject.Inject;
@@ -122,7 +122,7 @@ public class InvoiceDispatcher {
private static final Logger log = LoggerFactory.getLogger(InvoiceDispatcher.class);
private static final Ordering<DateTime> UPCOMING_NOTIFICATION_DATE_ORDERING = Ordering.natural();
- private final static Joiner JOINER_COMMA = Joiner.on(",");
+ private static final Joiner JOINER_COMMA = Joiner.on(",");
private static final TargetDateDryRunArguments TARGET_DATE_DRY_RUN_ARGUMENTS = new TargetDateDryRunArguments();
private final InvoiceGenerator generator;
@@ -171,28 +171,28 @@ public class InvoiceDispatcher {
public void processSubscriptionForInvoiceGeneration(final EffectiveSubscriptionInternalEvent transition,
final InternalCallContext context) throws InvoiceApiException {
final UUID subscriptionId = transition.getSubscriptionId();
- final DateTime targetDate = transition.getEffectiveTransitionTime();
+ final LocalDate targetDate = context.toLocalDate(transition.getEffectiveTransitionTime());
processSubscriptionForInvoiceGeneration(subscriptionId, targetDate, context);
}
- public void processSubscriptionForInvoiceGeneration(final UUID subscriptionId, final DateTime targetDate, final InternalCallContext context) throws InvoiceApiException {
+ public void processSubscriptionForInvoiceGeneration(final UUID subscriptionId, final LocalDate targetDate, final InternalCallContext context) throws InvoiceApiException {
processSubscriptionInternal(subscriptionId, targetDate, false, context);
}
- public void processSubscriptionForInvoiceNotification(final UUID subscriptionId, final DateTime targetDate, final InternalCallContext context) throws InvoiceApiException {
+ public void processSubscriptionForInvoiceNotification(final UUID subscriptionId, final LocalDate targetDate, final InternalCallContext context) throws InvoiceApiException {
final Invoice dryRunInvoice = processSubscriptionInternal(subscriptionId, targetDate, true, context);
if (dryRunInvoice != null && dryRunInvoice.getBalance().compareTo(BigDecimal.ZERO) > 0) {
final InvoiceNotificationInternalEvent event = new DefaultInvoiceNotificationInternalEvent(dryRunInvoice.getAccountId(), dryRunInvoice.getBalance(), dryRunInvoice.getCurrency(),
- targetDate, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
+ context.toUTCDateTime(targetDate), context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
try {
eventBus.post(event);
- } catch (EventBusException e) {
+ } catch (final EventBusException e) {
log.error("Failed to post event " + event, e);
}
}
}
- private Invoice processSubscriptionInternal(final UUID subscriptionId, final DateTime targetDate, final boolean dryRunForNotification, final InternalCallContext context) throws InvoiceApiException {
+ private Invoice processSubscriptionInternal(final UUID subscriptionId, final LocalDate targetDate, final boolean dryRunForNotification, final InternalCallContext context) throws InvoiceApiException {
try {
if (subscriptionId == null) {
log.error("Failed handling SubscriptionBase change.", new InvoiceApiException(ErrorCode.INVOICE_INVALID_TRANSITION));
@@ -209,7 +209,7 @@ public class InvoiceDispatcher {
}
}
- public Invoice processAccount(final UUID accountId, @Nullable final DateTime targetDate,
+ public Invoice processAccount(final UUID accountId, @Nullable final LocalDate targetDate,
@Nullable final DryRunArguments dryRunArguments, final InternalCallContext context) throws InvoiceApiException {
GlobalLock lock = null;
try {
@@ -228,13 +228,18 @@ public class InvoiceDispatcher {
return null;
}
- private Invoice processAccountWithLock(final UUID accountId, @Nullable final DateTime inputTargetDateTime,
+ private Invoice processAccountWithLock(final UUID accountId, @Nullable final LocalDate inputTargetDateMaybeNull,
@Nullable final DryRunArguments dryRunArguments, final InternalCallContext context) throws InvoiceApiException {
-
final boolean isDryRun = dryRunArguments != null;
- // A null inputTargetDateTime is only allowed in dryRun mode to have the system compute it
- Preconditions.checkArgument(inputTargetDateTime != null ||
- (dryRunArguments != null && DryRunType.UPCOMING_INVOICE.equals(dryRunArguments.getDryRunType())), "inputTargetDateTime is required in non dryRun mode");
+ final boolean upcomingInvoiceDryRun = isDryRun && DryRunType.UPCOMING_INVOICE.equals(dryRunArguments.getDryRunType());
+
+ LocalDate inputTargetDate = inputTargetDateMaybeNull;
+ // A null inputTargetDate is only allowed in dryRun mode to have the system compute it
+ if (inputTargetDate == null && !upcomingInvoiceDryRun) {
+ inputTargetDate = clock.getUTCToday();
+ }
+ Preconditions.checkArgument(inputTargetDate != null || upcomingInvoiceDryRun, "inputTargetDate is required in non dryRun mode");
+
try {
// Make sure to first set the BCD if needed then get the account object (to have the BCD set)
final BillingEventSet billingEvents = billingApi.getBillingEventsForAccountAndUpdateAccountBCD(accountId, dryRunArguments, context);
@@ -242,11 +247,11 @@ public class InvoiceDispatcher {
return null;
}
final Iterable<UUID> filteredSubscriptionIdsForDryRun = getFilteredSubscriptionIdsForDryRun(dryRunArguments, billingEvents);
- final List<DateTime> candidateDateTimes = (inputTargetDateTime != null) ?
- ImmutableList.of(inputTargetDateTime) :
- getUpcomingInvoiceCandidateDates(filteredSubscriptionIdsForDryRun, context);
- for (final DateTime curTargetDateTime : candidateDateTimes) {
- final Invoice invoice = processAccountWithLockAndInputTargetDate(accountId, curTargetDateTime, billingEvents, isDryRun, context);
+ final List<LocalDate> candidateTargetDates = (inputTargetDate != null) ?
+ ImmutableList.<LocalDate>of(inputTargetDate) :
+ getUpcomingInvoiceCandidateDates(filteredSubscriptionIdsForDryRun, context);
+ for (final LocalDate curTargetDate : candidateTargetDates) {
+ final Invoice invoice = processAccountWithLockAndInputTargetDate(accountId, curTargetDate, billingEvents, isDryRun, context);
if (invoice != null) {
filterInvoiceItemsForDryRun(filteredSubscriptionIdsForDryRun, invoice);
return invoice;
@@ -300,7 +305,7 @@ public class InvoiceDispatcher {
});
}
- private Invoice processAccountWithLockAndInputTargetDate(final UUID accountId, final DateTime targetDateTime,
+ private Invoice processAccountWithLockAndInputTargetDate(final UUID accountId, final LocalDate targetDate,
final BillingEventSet billingEvents, final boolean isDryRun, final InternalCallContext context) throws InvoiceApiException {
try {
final ImmutableAccountData account = accountApi.getImmutableAccountDataById(accountId, context);
@@ -316,12 +321,11 @@ public class InvoiceDispatcher {
}));
final Currency targetCurrency = account.getCurrency();
- 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, billingEvents.getAccountDateAndTimeZoneContext(), context);
+ final FutureAccountNotifications futureAccountNotifications = createNextFutureNotificationDate(invoiceWithMetadata, context);
//
@@ -329,9 +333,9 @@ public class InvoiceDispatcher {
//
if (invoice == null) {
if (isDryRun) {
- log.info("Generated null dryRun invoice for accountId {} and targetDate {} (targetDateTime {})", new Object[]{accountId, targetDate, targetDateTime});
+ log.info("Generated null dryRun invoice for accountId {} and targetDate {}", accountId, targetDate);
} else {
- log.info("Generated null invoice for accountId {} and targetDate {} (targetDateTime {})", new Object[]{accountId, targetDate, targetDateTime});
+ log.info("Generated null invoice for accountId {} and targetDate {}", accountId, targetDate);
final BusInternalEvent event = new DefaultNullInvoiceEvent(accountId, clock.getUTCToday(),
context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
@@ -371,7 +375,7 @@ public class InvoiceDispatcher {
final boolean isRealInvoiceWithNonEmptyItems = isThereAnyItemsLeft ? isRealInvoiceWithItems : false;
- setChargedThroughDates(billingEvents.getAccountDateAndTimeZoneContext(), invoice.getInvoiceItems(FixedPriceInvoiceItem.class), invoice.getInvoiceItems(RecurringInvoiceItem.class), context);
+ setChargedThroughDates(invoice.getInvoiceItems(FixedPriceInvoiceItem.class), invoice.getInvoiceItems(RecurringInvoiceItem.class), context);
if (InvoiceStatus.COMMITTED.equals(invoice.getStatus())) {
// TODO we should send bus events when we commit the invoice on disk in commitInvoice
@@ -384,14 +388,13 @@ public class InvoiceDispatcher {
} catch (final AccountApiException e) {
log.error("Failed handling SubscriptionBase change.", e);
return null;
- } catch (SubscriptionBaseApiException e) {
+ } catch (final SubscriptionBaseApiException e) {
log.error("Failed handling SubscriptionBase change.", e);
return null;
}
}
- private FutureAccountNotifications createNextFutureNotificationDate(final InvoiceWithMetadata invoiceWithMetadata, final AccountDateAndTimeZoneContext dateAndTimeZoneContext, final InternalCallContext context) {
-
+ private FutureAccountNotifications createNextFutureNotificationDate(final InvoiceWithMetadata invoiceWithMetadata, final InternalCallContext context) {
final Map<UUID, List<SubscriptionNotification>> result = new HashMap<UUID, List<SubscriptionNotification>>();
for (final UUID subscriptionId : invoiceWithMetadata.getPerSubscriptionFutureNotificationDates().keySet()) {
@@ -401,13 +404,13 @@ public class InvoiceDispatcher {
final SubscriptionFutureNotificationDates subscriptionFutureNotificationDates = invoiceWithMetadata.getPerSubscriptionFutureNotificationDates().get(subscriptionId);
// Add next recurring date if any
if (subscriptionFutureNotificationDates.getNextRecurringDate() != null) {
- perSubscriptionNotifications.add(new SubscriptionNotification(dateAndTimeZoneContext.computeUTCDateTimeFromLocalDate(subscriptionFutureNotificationDates.getNextRecurringDate()), true));
+ perSubscriptionNotifications.add(new SubscriptionNotification(context.toUTCDateTime(subscriptionFutureNotificationDates.getNextRecurringDate()), true));
}
// Add next usage dates if any
if (subscriptionFutureNotificationDates.getNextUsageDates() != null) {
- for (UsageDef usageDef : subscriptionFutureNotificationDates.getNextUsageDates().keySet()) {
+ for (final UsageDef usageDef : subscriptionFutureNotificationDates.getNextUsageDates().keySet()) {
final LocalDate nextNotificationDateForUsage = subscriptionFutureNotificationDates.getNextUsageDates().get(usageDef);
- final DateTime subscriptionUsageCallbackDate = nextNotificationDateForUsage != null ? dateAndTimeZoneContext.computeUTCDateTimeFromLocalDate(nextNotificationDateForUsage) : null;
+ final DateTime subscriptionUsageCallbackDate = nextNotificationDateForUsage != null ? context.toUTCDateTime(nextNotificationDateForUsage) : null;
perSubscriptionNotifications.add(new SubscriptionNotification(subscriptionUsageCallbackDate, true));
}
}
@@ -420,7 +423,7 @@ public class InvoiceDispatcher {
final boolean isInvoiceNotificationEnabled = invoiceConfig.getDryRunNotificationSchedule().getMillis() > 0;
if (isInvoiceNotificationEnabled) {
final Map<UUID, DateTime> upcomingPhasesForSubscriptions = subscriptionApi.getNextFutureEventForSubscriptions(SubscriptionBaseTransitionType.PHASE, context);
- for (UUID cur : upcomingPhasesForSubscriptions.keySet()) {
+ for (final UUID cur : upcomingPhasesForSubscriptions.keySet()) {
final DateTime curDate = upcomingPhasesForSubscriptions.get(cur);
List<SubscriptionNotification> resultValue = result.get(cur);
if (resultValue == null) {
@@ -430,7 +433,7 @@ public class InvoiceDispatcher {
result.put(cur, resultValue);
}
}
- return new FutureAccountNotifications(dateAndTimeZoneContext, result);
+ return new FutureAccountNotifications(result);
}
private Iterable<InvoiceItemModelDao> transformToInvoiceModelDao(final List<InvoiceItem> invoiceItems) {
@@ -464,7 +467,7 @@ public class InvoiceDispatcher {
tmp.append(String.format("Adjusting existing invoices %s with %d items for accountId %s and targetDate %s:\n",
adjustedInvoices, invoice.getNumberOfItems(), account.getId(), targetDate));
}
- for (InvoiceItem item : invoice.getInvoiceItems()) {
+ for (final InvoiceItem item : invoice.getInvoiceItems()) {
tmp.append(String.format("\t item = %s\n", item));
}
log.info(tmp.toString());
@@ -473,7 +476,7 @@ public class InvoiceDispatcher {
private boolean commitInvoiceAndSetFutureNotifications(final ImmutableAccountData account, final InvoiceModelDao invoiceModelDao,
final Iterable<InvoiceItemModelDao> invoiceItemModelDaos,
final FutureAccountNotifications futureAccountNotifications,
- boolean isRealInvoiceWithItems, final InternalCallContext context) throws SubscriptionBaseApiException, InvoiceApiException {
+ final boolean isRealInvoiceWithItems, final InternalCallContext context) throws SubscriptionBaseApiException, InvoiceApiException {
// We filter any zero amount for USAGE items prior we generate the invoice, which may leave us with an invoice with no items;
// we recompute the isRealInvoiceWithItems flag based on what is left (the call to invoice is still necessary to set the future notifications).
final Iterable<InvoiceItemModelDao> filteredInvoiceItemModelDaos = Iterables.filter(invoiceItemModelDaos, new Predicate<InvoiceItemModelDao>() {
@@ -545,13 +548,12 @@ public class InvoiceDispatcher {
return internalCallContextFactory.createCallContext(context);
}
- private void setChargedThroughDates(final AccountDateAndTimeZoneContext dateAndTimeZoneContext,
- final Collection<InvoiceItem> fixedPriceItems,
+ private void setChargedThroughDates(final Collection<InvoiceItem> fixedPriceItems,
final Collection<InvoiceItem> recurringItems,
final InternalCallContext context) throws SubscriptionBaseApiException {
final Map<UUID, DateTime> chargeThroughDates = new HashMap<UUID, DateTime>();
- addInvoiceItemsToChargeThroughDates(dateAndTimeZoneContext, chargeThroughDates, fixedPriceItems);
- addInvoiceItemsToChargeThroughDates(dateAndTimeZoneContext, chargeThroughDates, recurringItems);
+ addInvoiceItemsToChargeThroughDates(chargeThroughDates, fixedPriceItems, context);
+ addInvoiceItemsToChargeThroughDates(chargeThroughDates, recurringItems, context);
for (final UUID subscriptionId : chargeThroughDates.keySet()) {
if (subscriptionId != null) {
@@ -569,15 +571,15 @@ public class InvoiceDispatcher {
}
}
- private void addInvoiceItemsToChargeThroughDates(final AccountDateAndTimeZoneContext dateAndTimeZoneContext,
- final Map<UUID, DateTime> chargeThroughDates,
- final Collection<InvoiceItem> items) {
+ private void addInvoiceItemsToChargeThroughDates(final Map<UUID, DateTime> chargeThroughDates,
+ final Collection<InvoiceItem> items,
+ final InternalTenantContext internalTenantContext) {
for (final InvoiceItem item : items) {
final UUID subscriptionId = item.getSubscriptionId();
final LocalDate endDate = (item.getEndDate() != null) ? item.getEndDate() : item.getStartDate();
- final DateTime proposedChargedThroughDate = dateAndTimeZoneContext.computeUTCDateTimeFromLocalDate(endDate);
+ final DateTime proposedChargedThroughDate = internalTenantContext.toUTCDateTime(endDate);
if (chargeThroughDates.containsKey(subscriptionId)) {
if (chargeThroughDates.get(subscriptionId).isBefore(proposedChargedThroughDate)) {
chargeThroughDates.put(subscriptionId, proposedChargedThroughDate);
@@ -590,18 +592,12 @@ public class InvoiceDispatcher {
public static class FutureAccountNotifications {
- private final AccountDateAndTimeZoneContext dateAndTimeZoneContext;
private final Map<UUID, List<SubscriptionNotification>> notifications;
- public FutureAccountNotifications(final AccountDateAndTimeZoneContext dateAndTimeZoneContext, final Map<UUID, List<SubscriptionNotification>> notifications) {
- this.dateAndTimeZoneContext = dateAndTimeZoneContext;
+ public FutureAccountNotifications(final Map<UUID, List<SubscriptionNotification>> notifications) {
this.notifications = notifications;
}
- public AccountDateAndTimeZoneContext getAccountDateAndTimeZoneContext() {
- return dateAndTimeZoneContext;
- }
-
public Map<UUID, List<SubscriptionNotification>> getNotifications() {
return notifications;
}
@@ -626,11 +622,16 @@ public class InvoiceDispatcher {
}
}
- private List<DateTime> getUpcomingInvoiceCandidateDates(final Iterable<UUID> filteredSubscriptionIds, final InternalCallContext internalCallContext) {
+ private List<LocalDate> getUpcomingInvoiceCandidateDates(final Iterable<UUID> filteredSubscriptionIds, final InternalCallContext internalCallContext) {
final Iterable<DateTime> nextScheduledInvoiceDates = getNextScheduledInvoiceEffectiveDate(filteredSubscriptionIds, internalCallContext);
final Iterable<DateTime> nextScheduledSubscriptionsEventDates = subscriptionApi.getFutureNotificationsForAccount(internalCallContext);
- Iterables.concat(nextScheduledInvoiceDates, nextScheduledSubscriptionsEventDates);
- return UPCOMING_NOTIFICATION_DATE_ORDERING.sortedCopy(Iterables.concat(nextScheduledInvoiceDates, nextScheduledSubscriptionsEventDates));
+ return Lists.<DateTime, LocalDate>transform(UPCOMING_NOTIFICATION_DATE_ORDERING.sortedCopy(Iterables.<DateTime>concat(nextScheduledInvoiceDates, nextScheduledSubscriptionsEventDates)),
+ new Function<DateTime, LocalDate>() {
+ @Override
+ public LocalDate apply(final DateTime input) {
+ return internalCallContext.toLocalDate(input);
+ }
+ });
}
private Iterable<DateTime> getNextScheduledInvoiceEffectiveDate(final Iterable<UUID> filteredSubscriptionIds, final InternalCallContext internalCallContext) {
@@ -662,7 +663,7 @@ public class InvoiceDispatcher {
}
}
- private final static class TargetDateDryRunArguments implements DryRunArguments {
+ private static final class TargetDateDryRunArguments implements DryRunArguments {
@Override
public DryRunType getDryRunType() {
@@ -685,7 +686,7 @@ public class InvoiceDispatcher {
}
@Override
- public DateTime getEffectiveDate() {
+ public LocalDate getEffectiveDate() {
return null;
}
@@ -742,8 +743,7 @@ public class InvoiceDispatcher {
parentInvoice.addInvoiceItem(new InvoiceItemModelDao(parentInvoiceItem));
// build account date time zone
- final AccountDateAndTimeZoneContext accountDateTimeZone = new DefaultAccountDateAndTimeZoneContext(today, account.getTimeZone());
- final FutureAccountNotifications futureAccountNotifications = new FutureAccountNotifications(accountDateTimeZone, null);
+ final FutureAccountNotifications futureAccountNotifications = new FutureAccountNotifications(ImmutableMap.<UUID, List<SubscriptionNotification>>of());
invoiceDao.createInvoice(parentInvoice, parentInvoice.getInvoiceItems(), true, futureAccountNotifications, parentContext);
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceListener.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceListener.java
index 1c701e8..ff895d1 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceListener.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceListener.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -24,7 +26,6 @@ import org.killbill.billing.account.api.AccountInternalApi;
import org.killbill.billing.account.api.ImmutableAccountData;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.events.BlockingTransitionInternalEvent;
-import org.killbill.billing.events.EffectiveEntitlementInternalEvent;
import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
import org.killbill.billing.events.InvoiceCreationInternalEvent;
import org.killbill.billing.events.RepairSubscriptionInternalEvent;
@@ -68,11 +69,10 @@ public class InvoiceListener {
@AllowConcurrentEvents
@Subscribe
public void handleRepairSubscriptionEvent(final RepairSubscriptionInternalEvent event) {
-
try {
final InternalCallContext context = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "RepairBundle", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
- dispatcher.processAccount(event.getAccountId(), event.getEffectiveDate(), null, context);
- } catch (InvoiceApiException e) {
+ dispatcher.processAccount(event.getAccountId(), context.toLocalDate(event.getEffectiveDate()), null, context);
+ } catch (final InvoiceApiException e) {
log.error(e.getMessage());
}
}
@@ -80,30 +80,16 @@ public class InvoiceListener {
@AllowConcurrentEvents
@Subscribe
public void handleSubscriptionTransition(final EffectiveSubscriptionInternalEvent event) {
-
try {
// Skip future uncancel event
// Skip events which are marked as not being the last one
if (event.getTransitionType() == SubscriptionBaseTransitionType.UNCANCEL ||
- event.getTransitionType() == SubscriptionBaseTransitionType.MIGRATE_ENTITLEMENT
- || event.getRemainingEventsForUserOperation() > 0) {
+ event.getRemainingEventsForUserOperation() > 0) {
return;
}
final InternalCallContext context = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "SubscriptionBaseTransition", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
dispatcher.processSubscriptionForInvoiceGeneration(event, context);
- } catch (InvoiceApiException e) {
- log.error(e.getMessage());
- }
- }
-
- @AllowConcurrentEvents
- @Subscribe
- public void handleEntitlementTransition(final EffectiveEntitlementInternalEvent event) {
-
- try {
- final InternalCallContext context = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "SubscriptionBaseTransition", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
- dispatcher.processAccount(event.getAccountId(), event.getEffectiveTransitionTime(), null, context);
- } catch (InvoiceApiException e) {
+ } catch (final InvoiceApiException e) {
log.error(e.getMessage());
}
}
@@ -111,7 +97,6 @@ public class InvoiceListener {
@AllowConcurrentEvents
@Subscribe
public void handleBlockingStateTransition(final BlockingTransitionInternalEvent event) {
-
// We are only interested in blockBilling or unblockBilling transitions.
if (!event.isTransitionedToUnblockedBilling() && !event.isTransitionedToBlockedBilling()) {
return;
@@ -120,10 +105,10 @@ public class InvoiceListener {
try {
final InternalCallContext context = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "SubscriptionBaseTransition", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
final UUID accountId = accountApi.getByRecordId(event.getSearchKey1(), context);
- dispatcher.processAccount(accountId, clock.getUTCNow(), null, context);
- } catch (InvoiceApiException e) {
+ dispatcher.processAccount(accountId, null, null, context);
+ } catch (final InvoiceApiException e) {
log.error(e.getMessage());
- } catch (AccountApiException e) {
+ } catch (final AccountApiException e) {
log.error(e.getMessage());
}
}
@@ -131,8 +116,8 @@ public class InvoiceListener {
public void handleNextBillingDateEvent(final UUID subscriptionId, final DateTime eventDateTime, final UUID userToken, final Long accountRecordId, final Long tenantRecordId) {
try {
final InternalCallContext context = internalCallContextFactory.createInternalCallContext(tenantRecordId, accountRecordId, "Next Billing Date", CallOrigin.INTERNAL, UserType.SYSTEM, userToken);
- dispatcher.processSubscriptionForInvoiceGeneration(subscriptionId, eventDateTime, context);
- } catch (InvoiceApiException e) {
+ dispatcher.processSubscriptionForInvoiceGeneration(subscriptionId, context.toLocalDate(eventDateTime), context);
+ } catch (final InvoiceApiException e) {
log.error(e.getMessage());
}
}
@@ -140,8 +125,8 @@ public class InvoiceListener {
public void handleEventForInvoiceNotification(final UUID subscriptionId, final DateTime eventDateTime, final UUID userToken, final Long accountRecordId, final Long tenantRecordId) {
try {
final InternalCallContext context = internalCallContextFactory.createInternalCallContext(tenantRecordId, accountRecordId, "Next Billing Date", CallOrigin.INTERNAL, UserType.SYSTEM, userToken);
- dispatcher.processSubscriptionForInvoiceNotification(subscriptionId, eventDateTime, context);
- } catch (InvoiceApiException e) {
+ dispatcher.processSubscriptionForInvoiceNotification(subscriptionId, context.toLocalDate(eventDateTime), context);
+ } catch (final InvoiceApiException e) {
log.error(e.getMessage());
}
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceTagHandler.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceTagHandler.java
index cb6ed8c..c8bc558 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceTagHandler.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceTagHandler.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -18,18 +20,16 @@ package org.killbill.billing.invoice;
import java.util.UUID;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import org.killbill.billing.ObjectType;
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.events.ControlTagDeletionInternalEvent;
import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.util.callcontext.CallOrigin;
-import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.callcontext.UserType;
-import org.killbill.clock.Clock;
-import org.killbill.billing.events.ControlTagDeletionInternalEvent;
import org.killbill.billing.util.tag.ControlTagType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.google.common.eventbus.AllowConcurrentEvents;
import com.google.common.eventbus.Subscribe;
@@ -39,15 +39,12 @@ public class InvoiceTagHandler {
private static final Logger log = LoggerFactory.getLogger(InvoiceTagHandler.class);
- private final Clock clock;
private final InvoiceDispatcher dispatcher;
private final InternalCallContextFactory internalCallContextFactory;
@Inject
- public InvoiceTagHandler(final Clock clock,
- final InvoiceDispatcher dispatcher,
+ public InvoiceTagHandler(final InvoiceDispatcher dispatcher,
final InternalCallContextFactory internalCallContextFactory) {
- this.clock = clock;
this.dispatcher = dispatcher;
this.internalCallContextFactory = internalCallContextFactory;
}
@@ -55,7 +52,6 @@ public class InvoiceTagHandler {
@AllowConcurrentEvents
@Subscribe
public void process_AUTO_INVOICING_OFF_removal(final ControlTagDeletionInternalEvent event) {
-
if (event.getTagDefinition().getName().equals(ControlTagType.AUTO_INVOICING_OFF.toString()) && event.getObjectType() == ObjectType.ACCOUNT) {
final UUID accountId = event.getObjectId();
final InternalCallContext context = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "InvoiceTagHandler", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
@@ -65,8 +61,8 @@ public class InvoiceTagHandler {
private void processUnpaid_AUTO_INVOICING_OFF_invoices(final UUID accountId, final InternalCallContext context) {
try {
- dispatcher.processAccount(accountId, clock.getUTCNow(), null, context);
- } catch (InvoiceApiException e) {
+ dispatcher.processAccount(accountId, null, null, context);
+ } catch (final InvoiceApiException e) {
log.warn(String.format("Failed to process process removal AUTO_INVOICING_OFF for account %s", accountId), e);
}
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/CreditAdjInvoiceItem.java b/invoice/src/main/java/org/killbill/billing/invoice/model/CreditAdjInvoiceItem.java
index aa9938a..597c4a1 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/CreditAdjInvoiceItem.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/CreditAdjInvoiceItem.java
@@ -29,19 +29,15 @@ import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.util.UUIDs;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
public class CreditAdjInvoiceItem extends AdjInvoiceItem {
public CreditAdjInvoiceItem(final UUID invoiceId, final UUID accountId, final LocalDate date,
- final BigDecimal amount, final Currency currency) {
- this(UUIDs.randomUUID(), null, invoiceId, accountId, date, amount, currency);
+ @Nullable final String description, final BigDecimal amount, final Currency currency) {
+ this(UUIDs.randomUUID(), null, invoiceId, accountId, date, description, amount, currency);
}
- public CreditAdjInvoiceItem(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId, final LocalDate date,
- final BigDecimal amount, final Currency currency) {
- this(id, createdDate, invoiceId, accountId, date, null, amount, currency);
- }
public CreditAdjInvoiceItem(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId, final LocalDate date,
@Nullable final String description, final BigDecimal amount, final Currency currency) {
@@ -55,6 +51,6 @@ public class CreditAdjInvoiceItem extends AdjInvoiceItem {
@Override
public String getDescription() {
- return Objects.firstNonNull(description, "Invoice adjustment");
+ return MoreObjects.firstNonNull(description, "Invoice adjustment");
}
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
index 454c33f..a6cdd3e 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
@@ -249,7 +249,7 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
@Override
public BigDecimal getBalance() {
- return isWrittenOff || hasZeroParentBalance() ? BigDecimal.ZERO : InvoiceCalculatorUtils.computeInvoiceBalance(currency, invoiceItems, payments);
+ return hasZeroParentBalance() ? BigDecimal.ZERO : InvoiceCalculatorUtils.computeInvoiceBalance(currency, invoiceItems, payments, isWrittenOff() || isMigrationInvoice());
}
private boolean hasZeroParentBalance() {
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/ItemAdjInvoiceItem.java b/invoice/src/main/java/org/killbill/billing/invoice/model/ItemAdjInvoiceItem.java
index ff2387a..13656ed 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/ItemAdjInvoiceItem.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/ItemAdjInvoiceItem.java
@@ -34,16 +34,12 @@ import com.google.common.base.Objects;
public class ItemAdjInvoiceItem extends AdjInvoiceItem {
- public ItemAdjInvoiceItem(final InvoiceItem invoiceItem, final LocalDate effectiveDate,
+ public ItemAdjInvoiceItem(final InvoiceItem linkedInvoiceItem, final LocalDate effectiveDate,
final BigDecimal amount, final Currency currency) {
- this(UUIDs.randomUUID(), invoiceItem.getInvoiceId(), invoiceItem.getAccountId(), effectiveDate,
- amount, currency, invoiceItem.getId());
+ this(UUIDs.randomUUID(), null, linkedInvoiceItem.getInvoiceId(), linkedInvoiceItem.getAccountId(), effectiveDate,
+ linkedInvoiceItem.getDescription(), amount, currency, linkedInvoiceItem.getId());
}
- public ItemAdjInvoiceItem(final UUID id, final UUID invoiceId, final UUID accountId, final LocalDate startDate,
- final BigDecimal amount, final Currency currency, final UUID linkedItemId) {
- this(id, null, invoiceId, accountId, startDate, null, amount, currency, linkedItemId);
- }
public ItemAdjInvoiceItem(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId, final LocalDate startDate,
@Nullable final String description, final BigDecimal amount, final Currency currency, final UUID linkedItemId) {
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 955801c..9014bdb 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,7 +26,6 @@ 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.notificationq.api.NotificationEventWithMetadata;
import org.killbill.notificationq.api.NotificationQueue;
@@ -52,18 +51,20 @@ public class DefaultNextBillingDatePoster implements NextBillingDatePoster {
@Override
public void insertNextBillingNotificationFromTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final UUID accountId,
- final UUID subscriptionId, final DateTime futureNotificationTime, final AccountDateAndTimeZoneContext accountDateAndTimeZoneContext, final InternalCallContext internalCallContext) {
- insertNextBillingFromTransactionInternal(entitySqlDaoWrapperFactory, accountId, subscriptionId, Boolean.FALSE, futureNotificationTime, futureNotificationTime, accountDateAndTimeZoneContext, internalCallContext);
+ final UUID subscriptionId, final DateTime futureNotificationTime, final InternalCallContext internalCallContext) {
+ insertNextBillingFromTransactionInternal(entitySqlDaoWrapperFactory, subscriptionId, Boolean.FALSE, futureNotificationTime, futureNotificationTime, internalCallContext);
}
@Override
public void insertNextBillingDryRunNotificationFromTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final UUID accountId,
- 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);
+ final UUID subscriptionId, final DateTime futureNotificationTime, final DateTime targetDate, final InternalCallContext internalCallContext) {
+ insertNextBillingFromTransactionInternal(entitySqlDaoWrapperFactory, subscriptionId, Boolean.TRUE, futureNotificationTime, targetDate, internalCallContext);
}
- private void insertNextBillingFromTransactionInternal(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final UUID accountId,
- final UUID subscriptionId, final Boolean isDryRunForInvoiceNotification, final DateTime futureNotificationTime, final DateTime targetDate, final AccountDateAndTimeZoneContext accountDateAndTimeZoneContext, final InternalCallContext internalCallContext) {
+ private void insertNextBillingFromTransactionInternal(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory,
+ final UUID subscriptionId, final Boolean isDryRunForInvoiceNotification,
+ final DateTime futureNotificationTime, final DateTime targetDate,
+ final InternalCallContext internalCallContext) {
final NotificationQueue nextBillingQueue;
try {
nextBillingQueue = notificationQueueService.getNotificationQueue(DefaultInvoiceService.INVOICE_SERVICE_NAME,
@@ -77,8 +78,8 @@ public class DefaultNextBillingDatePoster implements NextBillingDatePoster {
final boolean isEventDryRunForNotifications = input.getEvent().isDryRunForInvoiceNotification() != null ?
input.getEvent().isDryRunForInvoiceNotification() : false;
- final LocalDate notificationEffectiveLocaleDate = accountDateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(futureNotificationTime);
- final LocalDate eventEffectiveLocaleDate = accountDateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(input.getEffectiveDate());
+ final LocalDate notificationEffectiveLocaleDate = internalCallContext.toLocalDate(futureNotificationTime);
+ final LocalDate eventEffectiveLocaleDate = internalCallContext.toLocalDate(input.getEffectiveDate());
return notificationEffectiveLocaleDate.compareTo(eventEffectiveLocaleDate) == 0 &&
((isDryRunForInvoiceNotification && isEventDryRunForNotifications) ||
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 011dc35..96836bd 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
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -22,15 +22,13 @@ 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;
public interface NextBillingDatePoster {
void insertNextBillingNotificationFromTransaction(EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, UUID accountId,
- UUID subscriptionId, DateTime futureNotificationTime, final AccountDateAndTimeZoneContext accountDateAndTimeZoneContext, InternalCallContext internalCallContext);
+ UUID subscriptionId, DateTime futureNotificationTime, InternalCallContext internalCallContext);
void insertNextBillingDryRunNotificationFromTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final UUID accountId,
- final UUID subscriptionId, final DateTime futureNotificationTime, final DateTime targetDate, final AccountDateAndTimeZoneContext accountDateAndTimeZoneContext, final InternalCallContext internalCallContext);
-
+ final UUID subscriptionId, final DateTime futureNotificationTime, final DateTime targetDate, final InternalCallContext internalCallContext);
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/notification/ParentInvoiceCommitmentPoster.java b/invoice/src/main/java/org/killbill/billing/invoice/notification/ParentInvoiceCommitmentPoster.java
index 58f4ca6..afd8816 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/notification/ParentInvoiceCommitmentPoster.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/notification/ParentInvoiceCommitmentPoster.java
@@ -25,7 +25,6 @@ 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.notificationq.api.NotificationEventWithMetadata;
import org.killbill.notificationq.api.NotificationQueue;
@@ -52,7 +51,6 @@ public class ParentInvoiceCommitmentPoster {
public void insertParentInvoiceFromTransactionInternal(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory,
final UUID invoiceId,
final DateTime futureNotificationTime,
- final AccountDateAndTimeZoneContext accountDateAndTimeZoneContext,
final InternalCallContext internalCallContext) {
final NotificationQueue commitInvoiceQueue;
try {
@@ -65,8 +63,8 @@ public class ParentInvoiceCommitmentPoster {
@Override
public boolean apply(final NotificationEventWithMetadata<ParentInvoiceCommitmentNotificationKey> input) {
- final LocalDate notificationEffectiveLocaleDate = accountDateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(futureNotificationTime);
- final LocalDate eventEffectiveLocaleDate = accountDateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(input.getEffectiveDate());
+ final LocalDate notificationEffectiveLocaleDate = internalCallContext.toLocalDate(futureNotificationTime);
+ final LocalDate eventEffectiveLocaleDate = internalCallContext.toLocalDate(input.getEffectiveDate());
return notificationEffectiveLocaleDate.compareTo(eventEffectiveLocaleDate) == 0;
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java b/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
index 29a86aa..0cf08e9 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
@@ -150,7 +150,7 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
if (!mergedInvoiceAdjustment.getCurrency().equals(item.getCurrency())) {
invoiceItems.add(item);
} else {
- mergedInvoiceAdjustment = new CreditAdjInvoiceItem(invoice.getId(), invoice.getAccountId(), invoice.getInvoiceDate(),
+ mergedInvoiceAdjustment = new CreditAdjInvoiceItem(invoice.getId(), invoice.getAccountId(), invoice.getInvoiceDate(), mergedInvoiceAdjustment.getDescription(),
mergedInvoiceAdjustment.getAmount().add(item.getAmount()), mergedInvoiceAdjustment.getCurrency());
}
}
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 592f05e..3e9c5e8 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
@@ -1,7 +1,8 @@
/*
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -28,6 +29,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
+import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.BillingMode;
import org.killbill.billing.catalog.api.CatalogApiException;
import org.killbill.billing.catalog.api.Currency;
@@ -41,7 +43,6 @@ 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;
@@ -74,7 +75,7 @@ public class ContiguousIntervalConsumableInArrear {
private final UUID invoiceId;
private final AtomicBoolean isBuilt;
private final LocalDate rawUsageStartDate;
- private final AccountDateAndTimeZoneContext dateAndTimeZoneContext;
+ private final InternalTenantContext internalTenantContext;
public ContiguousIntervalConsumableInArrear(final Usage usage,
final UUID accountId,
@@ -82,7 +83,7 @@ public class ContiguousIntervalConsumableInArrear {
final List<RawUsage> rawSubscriptionUsage,
final LocalDate targetDate,
final LocalDate rawUsageStartDate,
- final AccountDateAndTimeZoneContext dateAndTimeZoneContext) {
+ final InternalTenantContext internalTenantContext) {
this.usage = usage;
this.accountId = accountId;
this.invoiceId = invoiceId;
@@ -90,7 +91,7 @@ public class ContiguousIntervalConsumableInArrear {
this.rawSubscriptionUsage = rawSubscriptionUsage;
this.targetDate = targetDate;
this.rawUsageStartDate = rawUsageStartDate;
- this.dateAndTimeZoneContext = dateAndTimeZoneContext;
+ this.internalTenantContext = internalTenantContext;
this.billingEvents = Lists.newLinkedList();
this.transitionTimes = Lists.newLinkedList();
this.isBuilt = new AtomicBoolean(false);
@@ -112,11 +113,11 @@ public class ContiguousIntervalConsumableInArrear {
Preconditions.checkState((!closedInterval && billingEvents.size() >= 1) ||
(closedInterval && billingEvents.size() >= 2));
- final LocalDate startDate = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(billingEvents.get(0).getEffectiveDate());
+ final LocalDate startDate = internalTenantContext.toLocalDate(billingEvents.get(0).getEffectiveDate());
if (targetDate.isBefore(startDate)) {
return this;
}
- final LocalDate endDate = closedInterval ? dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(billingEvents.get(billingEvents.size() - 1).getEffectiveDate()) : targetDate;
+ final LocalDate endDate = closedInterval ? internalTenantContext.toLocalDate(billingEvents.get(billingEvents.size() - 1).getEffectiveDate()) : targetDate;
final BillingIntervalDetail bid = new BillingIntervalDetail(startDate, endDate, targetDate, getBCD(), usage.getBillingPeriod(), usage.getBillingMode());
@@ -230,15 +231,15 @@ public class ContiguousIntervalConsumableInArrear {
while (eventIt.hasNext()) {
final BillingEvent thisEvent = nextEvent;
nextEvent = eventIt.next();
- final LocalDate startDate = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(thisEvent.getEffectiveDate());
- final LocalDate endDate = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(nextEvent.getEffectiveDate());
+ final LocalDate startDate = internalTenantContext.toLocalDate(thisEvent.getEffectiveDate());
+ final LocalDate endDate = internalTenantContext.toLocalDate(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 = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(nextEvent.getEffectiveDate());
+ final LocalDate startDate = internalTenantContext.toLocalDate(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 6db0640..28b6aca 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
@@ -1,7 +1,8 @@
/*
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -27,6 +28,7 @@ import java.util.Set;
import java.util.UUID;
import org.joda.time.LocalDate;
+import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.BillingMode;
import org.killbill.billing.catalog.api.CatalogApiException;
import org.killbill.billing.catalog.api.Usage;
@@ -35,7 +37,6 @@ 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;
@@ -75,7 +76,7 @@ public class SubscriptionConsumableInArrear {
private final LocalDate targetDate;
private final List<RawUsage> rawSubscriptionUsage;
private final LocalDate rawUsageStartDate;
- private final AccountDateAndTimeZoneContext dateAndTimeZoneContext;
+ private final InternalTenantContext internalTenantContext;
public SubscriptionConsumableInArrear(final UUID accountId,
final UUID invoiceId,
@@ -83,14 +84,14 @@ public class SubscriptionConsumableInArrear {
final List<RawUsage> rawUsage,
final LocalDate targetDate,
final LocalDate rawUsageStartDate,
- final AccountDateAndTimeZoneContext dateAndTimeZoneContext) {
+ final InternalTenantContext internalTenantContext) {
this.accountId = accountId;
this.invoiceId = invoiceId;
this.subscriptionBillingEvents = subscriptionBillingEvents;
this.targetDate = targetDate;
this.rawUsageStartDate = rawUsageStartDate;
- this.dateAndTimeZoneContext= dateAndTimeZoneContext;
+ this.internalTenantContext = internalTenantContext;
// 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
@@ -148,7 +149,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, dateAndTimeZoneContext);
+ existingInterval = new ContiguousIntervalConsumableInArrear(usage, accountId, invoiceId, rawSubscriptionUsage, targetDate, rawUsageStartDate, internalTenantContext);
inFlightInArrearUsageIntervals.put(usage.getName(), existingInterval);
}
// Add billing event for that usage interval
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql b/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
index dd547fb..4a5e2dc 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
@@ -11,9 +11,9 @@ CREATE TABLE invoice_items (
bundle_id varchar(36),
subscription_id varchar(36),
description varchar(255),
- plan_name varchar(50),
- phase_name varchar(50),
- usage_name varchar(50),
+ plan_name varchar(255),
+ phase_name varchar(255),
+ usage_name varchar(255),
start_date date,
end_date date,
amount numeric(15,9) NOT NULL,
@@ -75,7 +75,6 @@ CREATE TABLE invoice_payments (
PRIMARY KEY(record_id)
) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
CREATE UNIQUE INDEX invoice_payments_id ON invoice_payments(id);
-CREATE UNIQUE INDEX idx_invoice_payments ON invoice_payments(payment_id, type);
CREATE INDEX invoice_payments_invoice_id ON invoice_payments(invoice_id);
CREATE INDEX invoice_payments_reversals ON invoice_payments(linked_invoice_payment_id);
CREATE INDEX invoice_payments_tenant_account_record_id ON invoice_payments(tenant_record_id, account_record_id);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java b/invoice/src/test/java/org/killbill/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
index 5784606..f5849cd 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -21,29 +23,27 @@ import java.util.Collection;
import java.util.List;
import java.util.UUID;
-import org.joda.time.DateTime;
import org.joda.time.LocalDate;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.Assert;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
import org.killbill.billing.account.api.Account;
-import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.invoice.InvoiceTestSuiteWithEmbeddedDB;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceApiException;
+import org.killbill.billing.invoice.api.InvoiceItem;
+import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.invoice.dao.InvoiceModelDao;
import org.killbill.billing.invoice.dao.InvoiceModelDaoHelper;
+import org.killbill.billing.invoice.model.RecurringInvoiceItem;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
-public class TestDefaultInvoiceMigrationApi extends InvoiceTestSuiteWithEmbeddedDB {
+import com.google.common.collect.ImmutableList;
- private final Logger log = LoggerFactory.getLogger(TestDefaultInvoiceMigrationApi.class);
+public class TestDefaultInvoiceMigrationApi extends InvoiceTestSuiteWithEmbeddedDB {
private LocalDate date_migrated;
- private DateTime date_regular;
+ private LocalDate date_regular;
private UUID accountId;
private UUID migrationInvoiceId;
@@ -57,7 +57,7 @@ public class TestDefaultInvoiceMigrationApi extends InvoiceTestSuiteWithEmbedded
public void beforeMethod() throws Exception {
super.beforeMethod();
date_migrated = clock.getUTCToday().minusYears(1);
- date_regular = clock.getUTCNow();
+ date_regular = clock.getUTCToday();
final Account account = invoiceUtil.createAccount(callContext);
accountId = account.getId();
@@ -66,13 +66,13 @@ public class TestDefaultInvoiceMigrationApi extends InvoiceTestSuiteWithEmbedded
}
private UUID createAndCheckMigrationInvoice(final UUID accountId) throws InvoiceApiException {
- final UUID migrationInvoiceId = migrationApi.createMigrationInvoice(accountId, date_migrated, MIGRATION_INVOICE_AMOUNT,
- MIGRATION_INVOICE_CURRENCY, callContext);
+
+ final InvoiceItem recurringItem = new RecurringInvoiceItem(null, accountId, null, null, "planName", "phaseName", new LocalDate(2016, 2, 1), new LocalDate(2016, 3, 1), MIGRATION_INVOICE_AMOUNT, MIGRATION_INVOICE_AMOUNT, MIGRATION_INVOICE_CURRENCY);
+ final UUID migrationInvoiceId = invoiceUserApi.createMigrationInvoice(accountId, date_migrated, ImmutableList.of(recurringItem), callContext);
Assert.assertNotNull(migrationInvoiceId);
//Double check it was created and values are correct
- final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(accountId, callContext);
- final InvoiceModelDao invoice = invoiceDao.getById(migrationInvoiceId, internalTenantContext);
+ final InvoiceModelDao invoice = invoiceDao.getById(migrationInvoiceId, internalCallContext);
Assert.assertNotNull(invoice);
Assert.assertEquals(invoice.getAccountId(), accountId);
@@ -80,7 +80,8 @@ public class TestDefaultInvoiceMigrationApi extends InvoiceTestSuiteWithEmbedded
// Assert.assertEquals(invoice.getTargetDate(),now);
Assert.assertEquals(invoice.getInvoiceItems().size(), 1);
Assert.assertEquals(invoice.getInvoiceItems().get(0).getAmount().compareTo(MIGRATION_INVOICE_AMOUNT), 0);
- Assert.assertEquals(InvoiceModelDaoHelper.getBalance(invoice).compareTo(MIGRATION_INVOICE_AMOUNT), 0);
+ Assert.assertEquals(invoice.getInvoiceItems().get(0).getType(), InvoiceItemType.RECURRING);
+ Assert.assertEquals(InvoiceModelDaoHelper.getBalance(invoice).compareTo(BigDecimal.ZERO), 0);
Assert.assertEquals(invoice.getCurrency(), MIGRATION_INVOICE_CURRENCY);
Assert.assertTrue(invoice.isMigrated());
@@ -89,7 +90,7 @@ public class TestDefaultInvoiceMigrationApi extends InvoiceTestSuiteWithEmbedded
@Test(groups = "slow")
public void testUserApiAccess() {
- final List<Invoice> byAccount = invoiceUserApi.getInvoicesByAccount(accountId, callContext);
+ final List<Invoice> byAccount = invoiceUserApi.getInvoicesByAccount(accountId, false, callContext);
Assert.assertEquals(byAccount.size(), 1);
Assert.assertEquals(byAccount.get(0).getId(), regularInvoiceId);
@@ -98,29 +99,18 @@ public class TestDefaultInvoiceMigrationApi extends InvoiceTestSuiteWithEmbedded
Assert.assertEquals(byAccountAndDate.get(0).getId(), regularInvoiceId);
final Collection<Invoice> unpaid = invoiceUserApi.getUnpaidInvoicesByAccountId(accountId, new LocalDate(date_regular.plusDays(1)), callContext);
- Assert.assertEquals(unpaid.size(), 2);
+ Assert.assertEquals(unpaid.size(), 1);
+ Assert.assertEquals(unpaid.iterator().next().getId(), regularInvoiceId);
}
-
// ACCOUNT balance should reflect total of migration and non-migration invoices
@Test(groups = "slow")
public void testBalance() throws InvoiceApiException {
- final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(accountId, callContext);
- final InvoiceModelDao migrationInvoice = invoiceDao.getById(migrationInvoiceId, internalTenantContext);
- final InvoiceModelDao regularInvoice = invoiceDao.getById(regularInvoiceId, internalTenantContext);
+ final InvoiceModelDao migrationInvoice = invoiceDao.getById(migrationInvoiceId, internalCallContext);
+ final InvoiceModelDao regularInvoice = invoiceDao.getById(regularInvoiceId, internalCallContext);
final BigDecimal balanceOfAllInvoices = InvoiceModelDaoHelper.getBalance(migrationInvoice).add(InvoiceModelDaoHelper.getBalance(regularInvoice));
final BigDecimal accountBalance = invoiceUserApi.getAccountBalance(accountId, callContext);
- log.info("ACCOUNT balance: " + accountBalance + " should equal the Balance Of All Invoices: " + balanceOfAllInvoices);
Assert.assertEquals(accountBalance.compareTo(balanceOfAllInvoices), 0);
}
-
- private boolean checkContains(final List<Invoice> invoices, final UUID invoiceId) {
- for (final Invoice invoice : invoices) {
- if (invoice.getId().equals(invoiceId)) {
- return true;
- }
- }
- return false;
- }
}
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java b/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
index 6461dd0..ebd4299 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -59,11 +61,9 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
super.beforeMethod();
final Account account = invoiceUtil.createAccount(callContext);
accountId = account.getId();
- invoiceId = invoiceUtil.generateRegularInvoice(account, clock.getUTCNow(), callContext);
+ invoiceId = invoiceUtil.generateRegularInvoice(account, null, callContext);
}
-
-
@Test(groups = "slow")
public void testPostExternalChargeOnNewInvoice() throws Exception {
// Initial account balance
@@ -202,14 +202,30 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
// Adjust the invoice for the full amount
final InvoiceItem creditInvoiceItem = invoiceUserApi.insertCreditForInvoice(accountId, invoiceId, invoiceBalance,
- clock.getUTCToday(), accountCurrency, callContext);
+ clock.getUTCToday(), accountCurrency, "some description", callContext);
+ Assert.assertEquals(creditInvoiceItem.getInvoiceId(), invoiceId);
+ Assert.assertEquals(creditInvoiceItem.getInvoiceItemType(), InvoiceItemType.CREDIT_ADJ);
+ Assert.assertEquals(creditInvoiceItem.getAccountId(), accountId);
+ Assert.assertEquals(creditInvoiceItem.getAmount().compareTo(invoiceBalance.negate()), 0);
+ Assert.assertEquals(creditInvoiceItem.getCurrency(), accountCurrency);
+ Assert.assertEquals(creditInvoiceItem.getDescription(), "some description");
+ Assert.assertNull(creditInvoiceItem.getLinkedItemId());
+
+ // Verify the adjusted invoice balance
+ final BigDecimal adjustedInvoiceBalance = invoiceUserApi.getInvoice(invoiceId, callContext).getBalance();
+ Assert.assertEquals(adjustedInvoiceBalance.compareTo(BigDecimal.ZERO), 0);
+ // Verify the adjusted account balance
+ final BigDecimal adjustedAccountBalance = invoiceUserApi.getAccountBalance(accountId, callContext);
+ Assert.assertEquals(adjustedAccountBalance, adjustedInvoiceBalance);
}
+
@Test(groups = "slow")
public void testCantAdjustInvoiceWithNegativeAmount() throws Exception {
try {
- invoiceUserApi.insertCreditForInvoice(accountId, invoiceId, BigDecimal.TEN.negate(), clock.getUTCToday(), accountCurrency, callContext);
+ invoiceUserApi.insertCreditForInvoice(accountId, invoiceId, BigDecimal.TEN.negate(), clock.getUTCToday(), accountCurrency,
+ null, callContext);
Assert.fail("Should not have been able to adjust an invoice with a negative amount");
} catch (InvoiceApiException e) {
Assert.assertEquals(e.getCode(), ErrorCode.CREDIT_AMOUNT_INVALID.getCode());
@@ -232,7 +248,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
// Adjust the invoice for the full amount
final InvoiceItem adjInvoiceItem = invoiceUserApi.insertInvoiceItemAdjustment(accountId, invoiceId, invoiceItem.getId(),
- clock.getUTCToday(), callContext);
+ clock.getUTCToday(), null, callContext);
Assert.assertEquals(adjInvoiceItem.getInvoiceId(), invoiceId);
Assert.assertEquals(adjInvoiceItem.getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
Assert.assertEquals(adjInvoiceItem.getAccountId(), accountId);
@@ -267,7 +283,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
final BigDecimal adjAmount = invoiceItem.getAmount().divide(BigDecimal.TEN);
final InvoiceItem adjInvoiceItem = invoiceUserApi.insertInvoiceItemAdjustment(accountId, invoiceId, invoiceItem.getId(),
clock.getUTCToday(), adjAmount, accountCurrency,
- callContext);
+ null, callContext);
Assert.assertEquals(adjInvoiceItem.getInvoiceId(), invoiceId);
Assert.assertEquals(adjInvoiceItem.getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
Assert.assertEquals(adjInvoiceItem.getAccountId(), accountId);
@@ -290,7 +306,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
try {
invoiceUserApi.insertInvoiceItemAdjustment(accountId, invoiceId, invoiceItem.getId(), clock.getUTCToday(),
- BigDecimal.TEN.negate(), accountCurrency, callContext);
+ BigDecimal.TEN.negate(), accountCurrency, null, callContext);
Assert.fail("Should not have been able to adjust an item with a negative amount");
} catch (InvoiceApiException e) {
Assert.assertEquals(e.getCode(), ErrorCode.INVOICE_ITEM_ADJUSTMENT_AMOUNT_SHOULD_BE_POSITIVE.getCode());
@@ -339,7 +355,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
// Adjust the invoice for the full amount
final BigDecimal creditAmount = BigDecimal.TEN;
final InvoiceItem creditInvoiceItem = invoiceUserApi.insertCreditForInvoice(accountId, null, creditAmount,
- clock.getUTCToday(), accountCurrency, callContext);
+ clock.getUTCToday(), accountCurrency, null, callContext);
final UUID invoiceId = creditInvoiceItem.getInvoiceId();
Invoice creditInvoice = invoiceUserApi.getInvoice(invoiceId, callContext);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
index 8a9659a..8abb9a9 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
@@ -46,6 +46,7 @@ import org.killbill.billing.catalog.api.Plan;
import org.killbill.billing.catalog.api.PlanPhase;
import org.killbill.billing.entity.EntityPersistenceException;
import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications;
+import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications.SubscriptionNotification;
import org.killbill.billing.invoice.InvoiceTestSuiteWithEmbeddedDB;
import org.killbill.billing.invoice.MockBillingEventSet;
import org.killbill.billing.invoice.api.Invoice;
@@ -69,9 +70,7 @@ import org.killbill.billing.junction.BillingEvent;
import org.killbill.billing.junction.BillingEventSet;
import org.killbill.billing.subscription.api.SubscriptionBase;
import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
-import org.killbill.billing.util.AccountDateAndTimeZoneContext;
import org.killbill.billing.util.currency.KillBillMoney;
-import org.killbill.billing.util.timezone.DefaultAccountDateAndTimeZoneContext;
import org.killbill.clock.ClockMock;
import org.mockito.Mockito;
import org.skife.jdbi.v2.exceptions.TransactionFailedException;
@@ -550,7 +549,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
endDate, rate1, rate1, Currency.USD);
invoiceUtil.createInvoiceItem(item1, context);
- final CreditAdjInvoiceItem creditItem = new CreditAdjInvoiceItem(invoice1.getId(), accountId, new LocalDate(), rate1.negate(), Currency.USD);
+ final CreditAdjInvoiceItem creditItem = new CreditAdjInvoiceItem(invoice1.getId(), accountId, new LocalDate(), null, rate1.negate(), Currency.USD);
invoiceUtil.createInvoiceItem(creditItem, context);
final BigDecimal balance = invoiceDao.getAccountBalance(accountId, context);
@@ -925,7 +924,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
assertEquals(cba.compareTo(new BigDecimal("0.00")), 0);
// FINALLY ISSUE A CREDIT ADJ
- final CreditAdjInvoiceItem creditItem = new CreditAdjInvoiceItem(invoice2.getId(), accountId, new LocalDate(), rate2.negate(), Currency.USD);
+ final CreditAdjInvoiceItem creditItem = new CreditAdjInvoiceItem(invoice2.getId(), accountId, new LocalDate(), null, rate2.negate(), Currency.USD);
invoiceUtil.createInvoiceItem(creditItem, context);
balance = invoiceDao.getAccountBalance(accountId, context);
assertEquals(balance.compareTo(new BigDecimal("0.00")), 0);
@@ -1721,6 +1720,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
invoiceModelDao.getId(),
accountId,
effectiveDate,
+ null,
// Note! The amount is negated here!
creditAmount.negate(),
invoiceModelDao.getCurrency());
@@ -1758,8 +1758,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
parentInvoice.addInvoiceItem(new InvoiceItemModelDao(parentInvoiceItem));
// build account date time zone
- final AccountDateAndTimeZoneContext accountDateTimeZone = new DefaultAccountDateAndTimeZoneContext(today, account.getTimeZone());
- final FutureAccountNotifications futureAccountNotifications = new FutureAccountNotifications(accountDateTimeZone, null);
+ final FutureAccountNotifications futureAccountNotifications = new FutureAccountNotifications(ImmutableMap.<UUID, List<SubscriptionNotification>>of());
invoiceDao.createInvoice(parentInvoice, parentInvoice.getInvoiceItems(), true, futureAccountNotifications, context);
final InvoiceModelDao parentDraftInvoice = invoiceDao.getParentDraftInvoice(parentAccountId, context);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
index 19778f5..e9fdfbf 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
@@ -33,7 +33,6 @@ 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.DefaultAccount;
import org.killbill.billing.catalog.DefaultPrice;
import org.killbill.billing.catalog.MockInternationalPrice;
import org.killbill.billing.catalog.MockPlan;
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestFixedAndRecurringInvoiceItemGenerator.java b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestFixedAndRecurringInvoiceItemGenerator.java
index fc2d83a..dfa95a0 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestFixedAndRecurringInvoiceItemGenerator.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestFixedAndRecurringInvoiceItemGenerator.java
@@ -23,7 +23,6 @@ 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.catalog.DefaultPrice;
@@ -45,8 +44,6 @@ import org.killbill.billing.junction.BillingEvent;
import org.killbill.billing.junction.BillingEventSet;
import org.killbill.billing.subscription.api.SubscriptionBase;
import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
-import org.killbill.billing.util.AccountDateAndTimeZoneContext;
-import org.killbill.billing.util.timezone.DefaultAccountDateAndTimeZoneContext;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -59,7 +56,6 @@ public class TestFixedAndRecurringInvoiceItemGenerator extends InvoiceTestSuiteN
private Account account;
private SubscriptionBase subscription;
- private AccountDateAndTimeZoneContext dateAndTimeZoneContext;
@Override
@BeforeMethod(groups = "fast")
@@ -69,7 +65,6 @@ public class TestFixedAndRecurringInvoiceItemGenerator extends InvoiceTestSuiteN
try {
account = invoiceUtil.createAccount(callContext);
subscription = invoiceUtil.createSubscription();
- dateAndTimeZoneContext = new DefaultAccountDateAndTimeZoneContext(new DateTime("2016-01-08T03:01:01.000Z"), DateTimeZone.UTC);
} catch (final Exception e) {
Assert.fail(e.getMessage());
}
@@ -92,7 +87,7 @@ public class TestFixedAndRecurringInvoiceItemGenerator extends InvoiceTestSuiteN
BillingMode.IN_ADVANCE, "Billing Event Desc", 1L,
SubscriptionBaseTransitionType.CREATE);
- assertFalse(fixedAndRecurringInvoiceItemGenerator.isSameDayAndSameSubscription(prevInvoiceItem, event, dateAndTimeZoneContext));
+ assertFalse(fixedAndRecurringInvoiceItemGenerator.isSameDayAndSameSubscription(prevInvoiceItem, event, internalCallContext));
}
@Test(groups = "fast")
@@ -115,7 +110,7 @@ public class TestFixedAndRecurringInvoiceItemGenerator extends InvoiceTestSuiteN
BillingMode.IN_ADVANCE, "Billing Event Desc", 1L,
SubscriptionBaseTransitionType.CREATE);
- assertFalse(fixedAndRecurringInvoiceItemGenerator.isSameDayAndSameSubscription(prevInvoiceItem, event, dateAndTimeZoneContext));
+ assertFalse(fixedAndRecurringInvoiceItemGenerator.isSameDayAndSameSubscription(prevInvoiceItem, event, internalCallContext));
}
@Test(groups = "fast")
@@ -138,7 +133,7 @@ public class TestFixedAndRecurringInvoiceItemGenerator extends InvoiceTestSuiteN
BillingMode.IN_ADVANCE, "Billing Event Desc", 1L,
SubscriptionBaseTransitionType.CREATE);
- assertFalse(fixedAndRecurringInvoiceItemGenerator.isSameDayAndSameSubscription(prevInvoiceItem, event, dateAndTimeZoneContext));
+ assertFalse(fixedAndRecurringInvoiceItemGenerator.isSameDayAndSameSubscription(prevInvoiceItem, event, internalCallContext));
}
@Test(groups = "fast")
@@ -161,7 +156,7 @@ public class TestFixedAndRecurringInvoiceItemGenerator extends InvoiceTestSuiteN
BillingMode.IN_ADVANCE, "Billing Event Desc", 1L,
SubscriptionBaseTransitionType.CREATE);
- assertTrue(fixedAndRecurringInvoiceItemGenerator.isSameDayAndSameSubscription(prevInvoiceItem, event, dateAndTimeZoneContext));
+ assertTrue(fixedAndRecurringInvoiceItemGenerator.isSameDayAndSameSubscription(prevInvoiceItem, event, internalCallContext));
}
@Test(groups = "fast")
@@ -193,7 +188,7 @@ public class TestFixedAndRecurringInvoiceItemGenerator extends InvoiceTestSuiteN
events.add(event2);
final List<InvoiceItem> proposedItems = new ArrayList<InvoiceItem>();
- fixedAndRecurringInvoiceItemGenerator.processFixedBillingEvents(invoiceId, account.getId(), events, targetDate, Currency.USD, proposedItems);
+ fixedAndRecurringInvoiceItemGenerator.processFixedBillingEvents(invoiceId, account.getId(), events, targetDate, Currency.USD, proposedItems, internalCallContext);
assertTrue(proposedItems.isEmpty());
}
@@ -226,7 +221,7 @@ public class TestFixedAndRecurringInvoiceItemGenerator extends InvoiceTestSuiteN
events.add(event2);
final List<InvoiceItem> proposedItems = new ArrayList<InvoiceItem>();
- fixedAndRecurringInvoiceItemGenerator.processFixedBillingEvents(invoiceId, account.getId(), events, targetDate, Currency.USD, proposedItems);
+ fixedAndRecurringInvoiceItemGenerator.processFixedBillingEvents(invoiceId, account.getId(), events, targetDate, Currency.USD, proposedItems, internalCallContext);
assertEquals(proposedItems.size(), 1);
assertEquals(proposedItems.get(0).getInvoiceItemType(), InvoiceItemType.FIXED);
assertEquals(proposedItems.get(0).getAmount(), fixedPriceAmount);
@@ -281,10 +276,10 @@ public class TestFixedAndRecurringInvoiceItemGenerator extends InvoiceTestSuiteN
events.add(event3);
final List<InvoiceItem> proposedItems = new ArrayList<InvoiceItem>();
- fixedAndRecurringInvoiceItemGenerator.processFixedBillingEvents(invoiceId, account.getId(), events, targetDate, Currency.USD, proposedItems);
+ fixedAndRecurringInvoiceItemGenerator.processFixedBillingEvents(invoiceId, account.getId(), events, targetDate, Currency.USD, proposedItems, internalCallContext);
assertEquals(proposedItems.size(), 1);
assertEquals(proposedItems.get(0).getInvoiceItemType(), InvoiceItemType.FIXED);
assertEquals(proposedItems.get(0).getAmount(), fixedPriceAmount3);
}
-}
\ No newline at end of file
+}
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/glue/TestInvoiceModuleNoDB.java b/invoice/src/test/java/org/killbill/billing/invoice/glue/TestInvoiceModuleNoDB.java
index a2f5751..cf36e03 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/glue/TestInvoiceModuleNoDB.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/glue/TestInvoiceModuleNoDB.java
@@ -35,6 +35,7 @@ import org.killbill.billing.currency.api.Rate;
import org.killbill.billing.invoice.dao.InvoiceDao;
import org.killbill.billing.invoice.dao.MockInvoiceDao;
import org.killbill.billing.mock.api.MockAccountUserApi;
+import org.killbill.billing.mock.glue.MockAccountModule;
import org.killbill.billing.mock.glue.MockNonEntityDaoModule;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.mockito.Mockito;
@@ -55,8 +56,7 @@ public class TestInvoiceModuleNoDB extends TestInvoiceModule {
install(new GuicyKillbillTestNoDBModule(configSource));
install(new MockNonEntityDaoModule(configSource));
- bind(AccountInternalApi.class).toInstance(Mockito.mock(AccountInternalApi.class));
- bind(AccountUserApi.class).to(MockAccountUserApi.class);
+ install(new MockAccountModule(configSource));
installCurrencyConversionApi();
}
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteNoDB.java b/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteNoDB.java
index 6945f02..dcd4a44 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteNoDB.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteNoDB.java
@@ -22,7 +22,6 @@ import org.killbill.billing.GuicyKillbillTestSuiteNoDB;
import org.killbill.billing.account.api.AccountInternalApi;
import org.killbill.billing.currency.api.CurrencyConversionApi;
import org.killbill.billing.invoice.api.InvoiceInternalApi;
-import org.killbill.billing.invoice.api.InvoiceMigrationApi;
import org.killbill.billing.invoice.api.InvoicePaymentApi;
import org.killbill.billing.invoice.api.InvoiceUserApi;
import org.killbill.billing.invoice.api.formatters.ResourceBundleFactory;
@@ -66,8 +65,6 @@ public abstract class InvoiceTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@Inject
protected InvoicePaymentApi invoicePaymentApi;
@Inject
- protected InvoiceMigrationApi migrationApi;
- @Inject
protected InvoiceGenerator generator;
@Inject
protected BillingInternalApi billingApi;
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java b/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java
index 346fa22..80552ed 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java
@@ -24,19 +24,15 @@ import org.killbill.billing.account.api.AccountUserApi;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.invoice.api.DefaultInvoiceService;
import org.killbill.billing.invoice.api.InvoiceInternalApi;
-import org.killbill.billing.invoice.api.InvoiceMigrationApi;
import org.killbill.billing.invoice.api.InvoicePaymentApi;
import org.killbill.billing.invoice.api.InvoiceService;
import org.killbill.billing.invoice.api.InvoiceUserApi;
-import org.killbill.billing.invoice.calculator.InvoiceCalculatorUtils;
import org.killbill.billing.invoice.dao.InvoiceDao;
import org.killbill.billing.invoice.generator.InvoiceGenerator;
import org.killbill.billing.invoice.glue.TestInvoiceModuleWithEmbeddedDb;
import org.killbill.billing.invoice.notification.NextBillingDateNotifier;
-import org.killbill.billing.invoice.plugin.api.InvoicePluginApi;
import org.killbill.billing.junction.BillingInternalApi;
import org.killbill.billing.lifecycle.api.BusService;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
import org.killbill.billing.util.api.TagUserApi;
@@ -75,8 +71,6 @@ public abstract class InvoiceTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
@Inject
protected InvoicePaymentApi invoicePaymentApi;
@Inject
- protected InvoiceMigrationApi migrationApi;
- @Inject
protected InvoiceGenerator generator;
@Inject
protected BillingInternalApi billingApi;
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 356cb3d..431a8dd 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/MockBillingEventSet.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/MockBillingEventSet.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -17,22 +19,16 @@
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 {
@@ -40,7 +36,6 @@ public class MockBillingEventSet extends TreeSet<BillingEvent> implements Billin
private boolean isAccountInvoiceOff;
private List<UUID> subscriptionIdsWithAutoInvoiceOff;
- private AccountDateAndTimeZoneContext accountDateAndTimeZoneContext;
public MockBillingEventSet() {
super();
@@ -48,28 +43,10 @@ public class MockBillingEventSet extends TreeSet<BillingEvent> implements Billin
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;
@@ -86,11 +63,6 @@ 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/model/TestItemAdjInvoiceItem.java b/invoice/src/test/java/org/killbill/billing/invoice/model/TestItemAdjInvoiceItem.java
index eccdb7f..a8f7d68 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/model/TestItemAdjInvoiceItem.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/model/TestItemAdjInvoiceItem.java
@@ -32,8 +32,8 @@ public class TestItemAdjInvoiceItem extends InvoiceTestSuiteNoDB {
@Test(groups = "fast")
public void testType() throws Exception {
- final InvoiceItem invoiceItem = new ItemAdjInvoiceItem(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(),
- new LocalDate(2010, 1, 1), new BigDecimal("7.00"), Currency.USD,
+ final InvoiceItem invoiceItem = new ItemAdjInvoiceItem(UUID.randomUUID(), null, UUID.randomUUID(), UUID.randomUUID(),
+ new LocalDate(2010, 1, 1), null, new BigDecimal("7.00"), Currency.USD,
UUID.randomUUID());
Assert.assertEquals(invoiceItem.getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
}
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 87a72de..5837359 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
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -23,7 +23,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
-import org.joda.time.DateTimeZone;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountApiException;
import org.killbill.billing.callcontext.InternalCallContext;
@@ -43,7 +42,6 @@ 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.DefaultAccountDateAndTimeZoneContext;
import org.killbill.clock.Clock;
import org.mockito.Mockito;
import org.testng.Assert;
@@ -108,9 +106,7 @@ public class InvoiceTestUtils {
}
Mockito.when(invoice.getInvoiceItems()).thenReturn(invoiceItems);
- 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);
+ invoiceDao.createInvoice(new InvoiceModelDao(invoice), invoiceModelItems, true, new FutureAccountNotifications(ImmutableMap.<UUID, List<SubscriptionNotification>>of()), internalCallContext);
return invoice;
}
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java b/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java
index a6a5359..1eedaf8 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java
@@ -122,7 +122,7 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
fixedItem.getStartDate(), fixedItem.getAmount(),
fixedItem.getCurrency());
final CreditAdjInvoiceItem creditAdjInvoiceItem = new CreditAdjInvoiceItem(fixedItem.getInvoiceId(), fixedItem.getAccountId(),
- fixedItem.getStartDate(), BigDecimal.ONE.negate(), fixedItem.getCurrency());
+ fixedItem.getStartDate(), null, BigDecimal.ONE.negate(), fixedItem.getCurrency());
final CreditBalanceAdjInvoiceItem creditBalanceAdjInvoiceItem2 = new CreditBalanceAdjInvoiceItem(fixedItem.getInvoiceId(), fixedItem.getAccountId(),
fixedItem.getStartDate(), creditAdjInvoiceItem.getAmount().negate(),
fixedItem.getCurrency());
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
index 2bdde60..079900b 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -84,7 +84,7 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
Mockito.when(billingApi.getBillingEventsForAccountAndUpdateAccountBCD(Mockito.<UUID>any(), Mockito.<DryRunArguments>any(), Mockito.<InternalCallContext>any())).thenReturn(events);
- final DateTime target = effectiveDate;
+ final LocalDate target = internalCallContext.toLocalDate(effectiveDate);
final InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
final InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountApi, billingApi, subscriptionApi, invoiceDao,
@@ -144,7 +144,7 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
internalCallContextFactory, invoiceNotifier, invoicePluginDispatcher, locker, busService.getBus(),
null, invoiceConfig, clock);
- final Invoice invoice = dispatcher.processAccount(account.getId(), new DateTime("2012-07-30T00:00:00.000Z"), null, context);
+ final Invoice invoice = dispatcher.processAccount(account.getId(), new LocalDate("2012-07-30"), null, context);
Assert.assertNotNull(invoice);
final List<InvoiceItem> invoiceItems = invoice.getInvoiceItems();
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 37572d0..66ea1e6 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -29,13 +29,17 @@ import javax.inject.Inject;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
+import org.killbill.billing.GuicyKillbillTestSuite;
+import org.killbill.billing.GuicyKillbillTestSuiteNoDB;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountApiException;
import org.killbill.billing.account.api.AccountData;
import org.killbill.billing.account.api.AccountInternalApi;
import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.account.api.ImmutableAccountInternalApi;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.callcontext.MutableInternalCallContext;
import org.killbill.billing.catalog.MockPlan;
import org.killbill.billing.catalog.MockPlanPhase;
import org.killbill.billing.catalog.api.BillingActionPolicy;
@@ -76,11 +80,12 @@ import org.killbill.billing.subscription.api.SubscriptionBase;
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.cache.CacheControllerDispatcher;
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.DefaultAccountDateAndTimeZoneContext;
+import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.clock.Clock;
import org.killbill.commons.locker.GlobalLocker;
import org.mockito.Mockito;
@@ -148,6 +153,7 @@ public class TestInvoiceHelper {
private final InvoiceGenerator generator;
private final BillingInternalApi billingApi;
private final AccountInternalApi accountApi;
+ private final ImmutableAccountInternalApi immutableAccountApi;
private final InvoicePluginDispatcher invoicePluginDispatcher;
private final AccountUserApi accountUserApi;
private final SubscriptionBaseInternalApi subscriptionApi;
@@ -155,7 +161,8 @@ public class TestInvoiceHelper {
private final InvoiceDao invoiceDao;
private final GlobalLocker locker;
private final Clock clock;
- private final InternalCallContext internalCallContext;
+ private final NonEntityDao nonEntityDao;
+ private final MutableInternalCallContext internalCallContext;
private final InternalCallContextFactory internalCallContextFactory;
private final InvoiceConfig invoiceConfig;
// Low level SqlDao used by the tests to directly insert rows
@@ -165,12 +172,13 @@ public class TestInvoiceHelper {
@Inject
public TestInvoiceHelper(final InvoiceGenerator generator, final IDBI dbi,
- final BillingInternalApi billingApi, final AccountInternalApi accountApi, final InvoicePluginDispatcher invoicePluginDispatcher, final AccountUserApi accountUserApi, final SubscriptionBaseInternalApi subscriptionApi, final BusService busService,
- final InvoiceDao invoiceDao, final GlobalLocker locker, final Clock clock, final InternalCallContext internalCallContext, final InvoiceConfig invoiceConfig,
+ final BillingInternalApi billingApi, final AccountInternalApi accountApi, final ImmutableAccountInternalApi immutableAccountApi, final InvoicePluginDispatcher invoicePluginDispatcher, final AccountUserApi accountUserApi, final SubscriptionBaseInternalApi subscriptionApi, final BusService busService,
+ final InvoiceDao invoiceDao, final GlobalLocker locker, final Clock clock, final NonEntityDao nonEntityDao, final CacheControllerDispatcher cacheControllerDispatcher, final MutableInternalCallContext internalCallContext, final InvoiceConfig invoiceConfig,
final InternalCallContextFactory internalCallContextFactory) {
this.generator = generator;
this.billingApi = billingApi;
this.accountApi = accountApi;
+ this.immutableAccountApi = immutableAccountApi;
this.invoicePluginDispatcher = invoicePluginDispatcher;
this.accountUserApi = accountUserApi;
this.subscriptionApi = subscriptionApi;
@@ -178,6 +186,7 @@ public class TestInvoiceHelper {
this.invoiceDao = invoiceDao;
this.locker = locker;
this.clock = clock;
+ this.nonEntityDao = nonEntityDao;
this.internalCallContext = internalCallContext;
this.internalCallContextFactory = internalCallContextFactory;
this.invoiceItemSqlDao = dbi.onDemand(InvoiceItemSqlDao.class);
@@ -185,7 +194,7 @@ public class TestInvoiceHelper {
this.invoiceConfig = invoiceConfig;
}
- public UUID generateRegularInvoice(final Account account, final DateTime targetDate, final CallContext callContext) throws Exception {
+ public UUID generateRegularInvoice(final Account account, final LocalDate targetDate, final CallContext callContext) throws Exception {
final SubscriptionBase subscription = Mockito.mock(SubscriptionBase.class);
Mockito.when(subscription.getId()).thenReturn(UUID.randomUUID());
Mockito.when(subscription.getBundleId()).thenReturn(new UUID(0L, 0L));
@@ -234,37 +243,30 @@ public class TestInvoiceHelper {
}
public Account createAccount(final CallContext callContext) throws AccountApiException {
- 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(true)
- .externalKey(UUID.randomUUID().toString().substring(1, 8))
- .billingCycleDayLocal(31)
- .currency(accountCurrency)
- .paymentMethodId(UUID.randomUUID())
- .timeZone(DateTimeZone.UTC)
- .build();
- return accountUserApi.createAccount(accountData, callContext);
- }
+ final Account 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(true)
+ .externalKey(UUID.randomUUID().toString().substring(1, 8))
+ .billingCycleDayLocal(31)
+ .currency(accountCurrency)
+ .paymentMethodId(UUID.randomUUID())
+ .timeZone(DateTimeZone.UTC)
+ .createdDate(clock.getUTCNow())
+ .build();
+
+ final Account account;
+ if (isFastTest()) {
+ account = GuicyKillbillTestSuiteNoDB.createMockAccount(accountData, accountUserApi, accountApi, immutableAccountApi, nonEntityDao, clock, internalCallContextFactory, callContext, internalCallContext);
+ } else {
+ account = accountUserApi.createAccount(accountData, callContext);
+ }
+
+ GuicyKillbillTestSuite.refreshCallContext(account.getId(), clock, internalCallContextFactory, callContext, internalCallContext);
- public Account createChildAccount(final UUID parentAccountId, final CallContext callContext) throws AccountApiException {
- 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(true)
- .externalKey(UUID.randomUUID().toString().substring(1, 8))
- .billingCycleDayLocal(31)
- .currency(accountCurrency)
- .paymentMethodId(UUID.randomUUID())
- .timeZone(DateTimeZone.UTC)
- .parentAccountId(parentAccountId)
- .isPaymentDelegatedToParent(false)
- .build();
- return accountUserApi.createAccount(accountData, callContext);
+ return account;
}
public void createInvoiceItem(final InvoiceItem invoiceItem, final InternalCallContext internalCallContext) throws EntityPersistenceException {
@@ -306,8 +308,7 @@ public class TestInvoiceHelper {
}));
// The test does not use the invoice callback notifier hence the empty map
- final DefaultAccountDateAndTimeZoneContext dateAndTimeZoneContext = new DefaultAccountDateAndTimeZoneContext(clock.getUTCNow(), DateTimeZone.UTC);
- invoiceDao.createInvoice(invoiceModelDao, invoiceItemModelDaos, isRealInvoiceWithItems, new FutureAccountNotifications(dateAndTimeZoneContext, ImmutableMap.<UUID, List<SubscriptionNotification>>of()), internalCallContext);
+ invoiceDao.createInvoice(invoiceModelDao, invoiceItemModelDaos, isRealInvoiceWithItems, new FutureAccountNotifications(ImmutableMap.<UUID, List<SubscriptionNotification>>of()), internalCallContext);
}
public void createPayment(final InvoicePayment invoicePayment, final InternalCallContext internalCallContext) {
@@ -462,7 +463,7 @@ public class TestInvoiceHelper {
}
@Override
- public DateTime getEffectiveDate() {
+ public LocalDate getEffectiveDate() {
return null;
}
@@ -481,4 +482,9 @@ public class TestInvoiceHelper {
return null;
}
}
+
+ // Unfortunately, this helper is shared across fast and slow tests
+ private boolean isFastTest() {
+ return Mockito.mockingDetails(accountApi).isMock();
+ }
}
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 ce4dcb6..19a4b45 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
@@ -1,7 +1,8 @@
/*
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -28,8 +29,6 @@ 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;
@@ -75,9 +74,7 @@ public class TestSubscriptionConsumableInArrear extends TestUsageInArrearBase {
LocalDate targetDate = new LocalDate(2013, 6, 23);
- 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 SubscriptionConsumableInArrear foo = new SubscriptionConsumableInArrear(accountId, invoiceId, billingEvents, ImmutableList.<RawUsage>of(), targetDate, new LocalDate(dt1, DateTimeZone.UTC), internalCallContext);
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 8f4d001..838854d 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
@@ -1,7 +1,8 @@
/*
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -41,8 +42,6 @@ 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;
@@ -72,10 +71,9 @@ public abstract class TestUsageInArrearBase extends InvoiceTestSuiteNoDB {
currency = Currency.BTC;
}
- protected ContiguousIntervalConsumableInArrear createContiguousIntervalConsumableInArrear(final DefaultUsage usage, List<RawUsage> rawUsages, final LocalDate targetDate, final boolean closedInterval, final BillingEvent... events) {
- 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) {
+ protected ContiguousIntervalConsumableInArrear createContiguousIntervalConsumableInArrear(final DefaultUsage usage, final 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()), internalCallContext);
+ for (final BillingEvent event : events) {
intervalConsumableInArrear.addBillingEvent(event);
}
intervalConsumableInArrear.build(closedInterval);
@@ -93,7 +91,7 @@ public abstract class TestUsageInArrearBase extends InvoiceTestSuiteNoDB {
}
protected DefaultTier createDefaultTier(final DefaultTieredBlock... blocks) {
- DefaultTier tier = new DefaultTier();
+ final DefaultTier tier = new DefaultTier();
tier.setBlocks(blocks);
return tier;
}
@@ -112,7 +110,7 @@ public abstract class TestUsageInArrearBase extends InvoiceTestSuiteNoDB {
return block;
}
- protected BillingEvent createMockBillingEvent(DateTime effectiveDate, BillingPeriod billingPeriod, final List<Usage> usages) {
+ protected BillingEvent createMockBillingEvent(final DateTime effectiveDate, final BillingPeriod billingPeriod, final List<Usage> usages) {
final BillingEvent result = Mockito.mock(BillingEvent.class);
Mockito.when(result.getCurrency()).thenReturn(Currency.BTC);
Mockito.when(result.getBillCycleDayLocal()).thenReturn(BCD);
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CreditJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CreditJson.java
index 5819a22..b3fd236 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CreditJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CreditJson.java
@@ -40,6 +40,8 @@ public class CreditJson extends JsonBase {
private final LocalDate effectiveDate;
@ApiModelProperty(dataType = "java.util.UUID", required = true)
private final String accountId;
+ private final String description;
+
@JsonCreator
public CreditJson(@JsonProperty("creditAmount") final BigDecimal creditAmount,
@@ -47,12 +49,14 @@ public class CreditJson extends JsonBase {
@JsonProperty("invoiceNumber") final String invoiceNumber,
@JsonProperty("effectiveDate") final LocalDate effectiveDate,
@JsonProperty("accountId") final String accountId,
+ @JsonProperty("description") final String description,
@JsonProperty("auditLogs") @Nullable final List<AuditLogJson> auditLogs) {
super(auditLogs);
this.creditAmount = creditAmount;
this.invoiceId = invoiceId;
this.invoiceNumber = invoiceNumber;
this.effectiveDate = effectiveDate;
+ this.description = description;
this.accountId = accountId;
}
@@ -63,6 +67,7 @@ public class CreditJson extends JsonBase {
this.invoiceId = toString(credit.getInvoiceId());
this.invoiceNumber = invoice.getInvoiceNumber().toString();
this.effectiveDate = credit.getStartDate();
+ this.description = credit.getDescription();
}
public CreditJson(final Invoice invoice, final InvoiceItem credit) {
@@ -89,6 +94,10 @@ public class CreditJson extends JsonBase {
return accountId;
}
+ public String getDescription() {
+ return description;
+ }
+
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
@@ -97,6 +106,7 @@ public class CreditJson extends JsonBase {
sb.append(", invoiceId=").append(invoiceId);
sb.append(", invoiceNumber='").append(invoiceNumber).append('\'');
sb.append(", effectiveDate=").append(effectiveDate);
+ sb.append(", description=").append(description);
sb.append(", accountId=").append(accountId);
sb.append('}');
return sb.toString();
@@ -123,6 +133,9 @@ public class CreditJson extends JsonBase {
if (invoiceNumber != null ? !invoiceNumber.equals(that.invoiceNumber) : that.invoiceNumber != null) {
return false;
}
+ if (description != null ? !description.equals(that.description) : that.description != null) {
+ return false;
+ }
if (!((effectiveDate == null && that.effectiveDate == null) ||
(effectiveDate != null && that.effectiveDate != null && effectiveDate.compareTo(that.effectiveDate) == 0))) {
return false;
@@ -135,6 +148,7 @@ public class CreditJson extends JsonBase {
public int hashCode() {
int result = creditAmount != null ? creditAmount.hashCode() : 0;
result = 31 * result + (invoiceId != null ? invoiceId.hashCode() : 0);
+ result = 31 * result + (description != null ? description.hashCode() : 0);
result = 31 * result + (invoiceNumber != null ? invoiceNumber.hashCode() : 0);
result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
return result;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PhasePriceOverrideJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PhasePriceOverrideJson.java
index 74b5c2b..1ce8cbb 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PhasePriceOverrideJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PhasePriceOverrideJson.java
@@ -114,11 +114,11 @@ public class PhasePriceOverrideJson {
return result;
}
- public static List<PlanPhasePriceOverride> toPlanPhasePriceOverrides(final List<PhasePriceOverrideJson> input, final PlanSpecifier spec, final Currency currency) {
- if (input == null || input.isEmpty()) {
+ public static List<PlanPhasePriceOverride> toPlanPhasePriceOverrides(final List<PhasePriceOverrideJson> priceOverrides, final PlanSpecifier spec, final Currency currency) {
+ if (priceOverrides == null || priceOverrides.isEmpty()) {
return ImmutableList.<PlanPhasePriceOverride>of();
}
- return ImmutableList.copyOf(Iterables.transform(input, new Function<PhasePriceOverrideJson, PlanPhasePriceOverride>() {
+ return ImmutableList.copyOf(Iterables.transform(priceOverrides, new Function<PhasePriceOverrideJson, PlanPhasePriceOverride>() {
@Nullable
@Override
public PlanPhasePriceOverride apply(@Nullable final PhasePriceOverrideJson input) {
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionJson.java
index c860f33..adb80de 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionJson.java
@@ -24,6 +24,7 @@ import java.util.List;
import javax.annotation.Nullable;
import org.joda.time.LocalDate;
+import org.killbill.billing.ObjectType;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.PlanPhase;
import org.killbill.billing.catalog.api.PriceList;
@@ -31,6 +32,7 @@ import org.killbill.billing.catalog.api.Product;
import org.killbill.billing.entitlement.api.Subscription;
import org.killbill.billing.entitlement.api.SubscriptionEvent;
import org.killbill.billing.util.audit.AccountAuditLogs;
+import org.killbill.billing.util.audit.AuditLog;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -72,7 +74,6 @@ public class SubscriptionJson extends JsonBase {
private final String eventId;
private final String billingPeriod;
- private final LocalDate requestedDate;
private final LocalDate effectiveDate;
private final String product;
private final String priceList;
@@ -87,7 +88,6 @@ public class SubscriptionJson extends JsonBase {
@JsonCreator
public EventSubscriptionJson(@JsonProperty("eventId") final String eventId,
@JsonProperty("billingPeriod") final String billingPeriod,
- @JsonProperty("requestedDt") final LocalDate requestedDate,
@JsonProperty("effectiveDt") final LocalDate effectiveDate,
@JsonProperty("product") final String product,
@JsonProperty("priceList") final String priceList,
@@ -101,7 +101,6 @@ public class SubscriptionJson extends JsonBase {
super(auditLogs);
this.eventId = eventId;
this.billingPeriod = billingPeriod;
- this.requestedDate = requestedDate;
this.effectiveDate = effectiveDate;
this.product = product;
this.priceList = priceList;
@@ -114,14 +113,14 @@ public class SubscriptionJson extends JsonBase {
}
public EventSubscriptionJson(final SubscriptionEvent subscriptionEvent, @Nullable final AccountAuditLogs accountAuditLogs) {
- super(toAuditLogJson(accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForSubscriptionEvent(subscriptionEvent.getId())));
+
+ super(toAuditLogJson(getAuditLogsForSubscriptionEvent(subscriptionEvent, accountAuditLogs)));
final BillingPeriod billingPeriod = subscriptionEvent.getNextBillingPeriod() != null ? subscriptionEvent.getNextBillingPeriod() : subscriptionEvent.getPrevBillingPeriod();
final Product product = subscriptionEvent.getNextProduct() != null ? subscriptionEvent.getNextProduct() : subscriptionEvent.getPrevProduct();
final PriceList priceList = subscriptionEvent.getNextPriceList() != null ? subscriptionEvent.getNextPriceList() : subscriptionEvent.getPrevPriceList();
final PlanPhase phase = subscriptionEvent.getNextPhase() != null ? subscriptionEvent.getNextPhase() : subscriptionEvent.getPrevPhase();
this.eventId = subscriptionEvent.getId().toString();
this.billingPeriod = billingPeriod != null ? billingPeriod.toString() : null;
- this.requestedDate = subscriptionEvent.getRequestedDate();
this.effectiveDate = subscriptionEvent.getEffectiveDate();
this.product = product != null ? product.getName() : null;
this.priceList = priceList != null ? priceList.getName() : null;
@@ -133,6 +132,20 @@ public class SubscriptionJson extends JsonBase {
this.phase = phase != null ? phase.getName() : null;
}
+
+ private static List<AuditLog> getAuditLogsForSubscriptionEvent(final SubscriptionEvent subscriptionEvent, @Nullable final AccountAuditLogs accountAuditLogs) {
+ if (accountAuditLogs == null) {
+ return null;
+ }
+ final ObjectType subscriptionEventObjectType = subscriptionEvent.getSubscriptionEventType().getObjectType();
+ if (subscriptionEventObjectType == ObjectType.SUBSCRIPTION_EVENT) {
+ return accountAuditLogs.getAuditLogsForSubscriptionEvent(subscriptionEvent.getId());
+ } else if (subscriptionEventObjectType == ObjectType.BLOCKING_STATES) {
+ return accountAuditLogs.getAuditLogsForBlockingState(subscriptionEvent.getId());
+ }
+ throw new IllegalStateException("Unepxected objectType " + subscriptionEventObjectType + " for SubscriptionEvent " + subscriptionEvent.getId());
+ }
+
public String getEventId() {
return eventId;
}
@@ -141,10 +154,6 @@ public class SubscriptionJson extends JsonBase {
return billingPeriod;
}
- public LocalDate getRequestedDate() {
- return requestedDate;
- }
-
public LocalDate getEffectiveDate() {
return effectiveDate;
}
@@ -186,7 +195,6 @@ public class SubscriptionJson extends JsonBase {
final StringBuilder sb = new StringBuilder("EventSubscriptionJson{");
sb.append("eventId='").append(eventId).append('\'');
sb.append(", billingPeriod='").append(billingPeriod).append('\'');
- sb.append(", requestedDate=").append(requestedDate);
sb.append(", effectiveDate=").append(effectiveDate);
sb.append(", product='").append(product).append('\'');
sb.append(", priceList='").append(priceList).append('\'');
@@ -238,9 +246,6 @@ public class SubscriptionJson extends JsonBase {
if (product != null ? !product.equals(that.product) : that.product != null) {
return false;
}
- if (requestedDate != null ? requestedDate.compareTo(that.requestedDate) != 0 : that.requestedDate != null) {
- return false;
- }
if (serviceName != null ? !serviceName.equals(that.serviceName) : that.serviceName != null) {
return false;
}
@@ -255,7 +260,6 @@ public class SubscriptionJson extends JsonBase {
public int hashCode() {
int result = eventId != null ? eventId.hashCode() : 0;
result = 31 * result + (billingPeriod != null ? billingPeriod.hashCode() : 0);
- result = 31 * result + (requestedDate != null ? requestedDate.hashCode() : 0);
result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
result = 31 * result + (product != null ? product.hashCode() : 0);
result = 31 * result + (priceList != null ? priceList.hashCode() : 0);
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/IllegalStateExceptionMapper.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/IllegalStateExceptionMapper.java
new file mode 100644
index 0000000..1b9167c
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/IllegalStateExceptionMapper.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016 Groupon, Inc
+ * Copyright 2016 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.jaxrs.mappers;
+
+import javax.inject.Singleton;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.killbill.billing.util.callcontext.InternalCallContextFactory.ObjectDoesNotExist;
+
+@Singleton
+@Provider
+public class IllegalStateExceptionMapper extends ExceptionMapperBase implements ExceptionMapper<IllegalStateException> {
+
+ private final UriInfo uriInfo;
+
+ public IllegalStateExceptionMapper(@Context final UriInfo uriInfo) {
+ this.uriInfo = uriInfo;
+ }
+
+ @Override
+ public Response toResponse(final IllegalStateException exception) {
+ if (exception instanceof ObjectDoesNotExist) {
+ // Likely object for wrong tenant
+ return buildNotFoundResponse(exception, uriInfo);
+ } else {
+ return buildInternalErrorResponse(exception, uriInfo);
+ }
+ }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
index ee73b60..fe24c85 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
@@ -396,7 +396,7 @@ public class AccountResource extends JaxRsResourceBase {
final Callable<List<Invoice>> invoicesCallable = new Callable<List<Invoice>>() {
@Override
public List<Invoice> call() throws Exception {
- return invoiceApi.getInvoicesByAccount(account.getId(), tenantContext);
+ return invoiceApi.getInvoicesByAccount(account.getId(), false, tenantContext);
}
};
final Callable<List<InvoicePayment>> invoicePaymentsCallable = new Callable<List<InvoicePayment>>() {
@@ -587,6 +587,7 @@ public class AccountResource extends JaxRsResourceBase {
@ApiResponse(code = 404, message = "Account not found")})
public Response getInvoices(@PathParam("accountId") final String accountIdString,
@QueryParam(QUERY_INVOICE_WITH_ITEMS) @DefaultValue("false") final boolean withItems,
+ @QueryParam(QUERY_WITH_MIGRATION_INVOICES) @DefaultValue("false") final boolean withMigrationInvoices,
@QueryParam(QUERY_UNPAID_INVOICES_ONLY) @DefaultValue("false") final boolean unpaidInvoicesOnly,
@QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
@javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException {
@@ -598,7 +599,7 @@ public class AccountResource extends JaxRsResourceBase {
final List<Invoice> invoices = unpaidInvoicesOnly ?
new ArrayList<Invoice>(invoiceApi.getUnpaidInvoicesByAccountId(accountId, null, tenantContext)) :
- invoiceApi.getInvoicesByAccount(accountId, tenantContext);
+ invoiceApi.getInvoicesByAccount(accountId, withMigrationInvoices, tenantContext);
final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(accountId, auditMode.getLevel(), tenantContext);
@@ -689,7 +690,7 @@ public class AccountResource extends JaxRsResourceBase {
// then we apply some credit on the account.
//
if (externalPayment && remainingRequestPayment.compareTo(BigDecimal.ZERO) > 0) {
- invoiceApi.insertCredit(account.getId(), remainingRequestPayment, clock.getUTCToday(), account.getCurrency(), true, callContext);
+ invoiceApi.insertCredit(account.getId(), remainingRequestPayment, clock.getUTCToday(), account.getCurrency(), true, "pay all invoices", callContext);
}
return Response.status(Status.OK).build();
}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java
index 7f3432a..7658730 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java
@@ -205,12 +205,10 @@ public class BundleResource extends JaxRsResourceBase {
@HeaderParam(HDR_REASON) final String reason,
@HeaderParam(HDR_COMMENT) final String comment,
@javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException, EntitlementApiException, AccountApiException {
-
final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
final CallContext callContext = context.createContext(createdBy, reason, comment, request);
final UUID bundleId = UUID.fromString(id);
- final SubscriptionBundle bundle = subscriptionApi.getSubscriptionBundle(bundleId, callContext);
- final LocalDate inputLocalDate = toLocalDate(bundle.getAccountId(), requestedDate, callContext);
+ final LocalDate inputLocalDate = toLocalDate(requestedDate, callContext);
entitlementApi.pause(bundleId, inputLocalDate, pluginProperties, callContext);
return Response.status(Status.OK).build();
}
@@ -230,12 +228,10 @@ public class BundleResource extends JaxRsResourceBase {
@HeaderParam(HDR_REASON) final String reason,
@HeaderParam(HDR_COMMENT) final String comment,
@javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException, EntitlementApiException, AccountApiException {
-
final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
final CallContext callContext = context.createContext(createdBy, reason, comment, request);
final UUID bundleId = UUID.fromString(id);
- final SubscriptionBundle bundle = subscriptionApi.getSubscriptionBundle(bundleId, callContext);
- final LocalDate inputLocalDate = toLocalDate(bundle.getAccountId(), requestedDate, callContext);
+ final LocalDate inputLocalDate = toLocalDate(requestedDate, callContext);
entitlementApi.resume(bundleId, inputLocalDate, pluginProperties, callContext);
return Response.status(Status.OK).build();
}
@@ -362,7 +358,7 @@ public class BundleResource extends JaxRsResourceBase {
final UUID bundleId = UUID.fromString(id);
final SubscriptionBundle bundle = subscriptionApi.getSubscriptionBundle(bundleId, callContext);
- final LocalDate inputLocalDate = toLocalDate(bundle.getAccountId(), requestedDate, callContext);
+ final LocalDate inputLocalDate = toLocalDate(requestedDate, callContext);
final UUID newBundleId = entitlementApi.transferEntitlementsOverrideBillingPolicy(bundle.getAccountId(), UUID.fromString(json.getAccountId()), bundle.getExternalKey(), inputLocalDate, policy, pluginProperties, callContext);
return uriBuilder.buildResponse(BundleResource.class, "getBundle", newBundleId, uriInfo.getBaseUri().toString());
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CreditResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CreditResource.java
index d9f2585..0be0e19 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CreditResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CreditResource.java
@@ -124,11 +124,11 @@ public class CreditResource extends JaxRsResourceBase {
if (json.getInvoiceId() != null) {
// Apply an invoice level credit
credit = invoiceUserApi.insertCreditForInvoice(account.getId(), UUID.fromString(json.getInvoiceId()), json.getCreditAmount(),
- effectiveDate, account.getCurrency(), callContext);
+ effectiveDate, account.getCurrency(), json.getDescription(), callContext);
} else {
// Apply a account level credit
credit = invoiceUserApi.insertCredit(account.getId(), json.getCreditAmount(), effectiveDate,
- account.getCurrency(), autoCommit, callContext);
+ account.getCurrency(), autoCommit, json.getDescription(), callContext);
}
return uriBuilder.buildResponse(uriInfo, CreditResource.class, "getCredit", credit.getId());
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
index 58ab21b..4a09004 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
@@ -52,8 +52,6 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import org.killbill.billing.ErrorCode;
import org.killbill.billing.ObjectType;
@@ -106,7 +104,6 @@ import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.TenantContext;
import org.killbill.billing.util.entity.Pagination;
import org.killbill.clock.Clock;
-import org.killbill.clock.ClockUtil;
import org.killbill.commons.metrics.TimedResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -303,7 +300,7 @@ public class InvoiceResource extends JaxRsResourceBase {
@javax.ws.rs.core.Context final HttpServletRequest request,
@javax.ws.rs.core.Context final UriInfo uriInfo) throws AccountApiException, InvoiceApiException {
final CallContext callContext = context.createContext(createdBy, reason, comment, request);
- final LocalDate inputDate = toLocalDate(UUID.fromString(accountId), targetDate, callContext);
+ final LocalDate inputDate = toLocalDate(targetDate, callContext);
try {
final Invoice generatedInvoice = invoiceApi.triggerInvoiceGeneration(UUID.fromString(accountId), inputDate, null,
@@ -319,6 +316,31 @@ public class InvoiceResource extends JaxRsResourceBase {
@TimedResource
@POST
+ @Path("/" + MIGRATION + "/{accountId:" + UUID_PATTERN + "}")
+ @Consumes(APPLICATION_JSON)
+ @Produces(APPLICATION_JSON)
+ @ApiOperation(value = "Create a migration invoice", response = InvoiceJson.class)
+ @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid account id or target datetime supplied")})
+ public Response createMigrationInvoice(final Iterable<InvoiceItemJson> items,
+ @PathParam("accountId") final String accountId,
+ @Nullable @QueryParam(QUERY_TARGET_DATE) final String targetDate,
+ @HeaderParam(HDR_CREATED_BY) final String createdBy,
+ @HeaderParam(HDR_REASON) final String reason,
+ @HeaderParam(HDR_COMMENT) final String comment,
+ @javax.ws.rs.core.Context final HttpServletRequest request,
+ @javax.ws.rs.core.Context final UriInfo uriInfo) throws AccountApiException, InvoiceApiException {
+ final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+
+ final Account account = accountUserApi.getAccountById(UUID.fromString(accountId), callContext);
+ final Iterable<InvoiceItem> sanitizedInvoiceItems = validateSanitizeAndTranformInputItems(account.getCurrency(), items);
+ final LocalDate resolvedTargetDate = toLocalDateDefaultToday(account, targetDate, callContext);
+ final UUID invoiceId = invoiceApi.createMigrationInvoice(UUID.fromString(accountId), resolvedTargetDate, sanitizedInvoiceItems, callContext);
+ return uriBuilder.buildResponse(uriInfo, InvoiceResource.class, "getInvoice", invoiceId);
+ }
+
+
+ @TimedResource
+ @POST
@Path("/" + DRY_RUN)
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@@ -340,10 +362,10 @@ public class InvoiceResource extends JaxRsResourceBase {
} else if (DryRunType.SUBSCRIPTION_ACTION.name().equals(dryRunSubscriptionSpec.getDryRunType()) && dryRunSubscriptionSpec.getEffectiveDate() != null) {
inputDate = dryRunSubscriptionSpec.getEffectiveDate();
} else {
- inputDate = toLocalDate(UUID.fromString(accountId), targetDate, callContext);
+ inputDate = toLocalDate(targetDate, callContext);
}
} else {
- inputDate = toLocalDate(UUID.fromString(accountId), targetDate, callContext);
+ inputDate = toLocalDate(targetDate, callContext);
}
// Passing a null or empty body means we are trying to generate an invoice with a (future) targetDate
@@ -367,7 +389,7 @@ public class InvoiceResource extends JaxRsResourceBase {
final Account account = accountUserApi.getAccountById(UUID.fromString(accountId), callContext);
- final DryRunArguments dryRunArguments = new DefaultDryRunArguments(dryRunSubscriptionSpec, account.getTimeZone(), account.getCurrency(), clock);
+ final DryRunArguments dryRunArguments = new DefaultDryRunArguments(dryRunSubscriptionSpec, account);
try {
final Invoice generatedInvoice = invoiceApi.triggerInvoiceGeneration(UUID.fromString(accountId), inputDate, dryRunArguments,
callContext);
@@ -427,13 +449,14 @@ public class InvoiceResource extends JaxRsResourceBase {
final CallContext callContext = context.createContext(createdBy, reason, comment, request);
final UUID accountId = UUID.fromString(json.getAccountId());
- final LocalDate requestedDate = toLocalDate(accountId, requestedDateTimeString, callContext);
+ final LocalDate requestedDate = toLocalDateDefaultToday(accountId, requestedDateTimeString, callContext);
final InvoiceItem adjustmentItem;
if (json.getAmount() == null) {
adjustmentItem = invoiceApi.insertInvoiceItemAdjustment(accountId,
UUID.fromString(invoiceId),
UUID.fromString(json.getInvoiceItemId()),
requestedDate,
+ json.getDescription(),
callContext);
} else {
adjustmentItem = invoiceApi.insertInvoiceItemAdjustment(accountId,
@@ -442,6 +465,7 @@ public class InvoiceResource extends JaxRsResourceBase {
requestedDate,
json.getAmount(),
json.getCurrency(),
+ json.getDescription(),
callContext);
}
@@ -471,20 +495,11 @@ public class InvoiceResource extends JaxRsResourceBase {
final CallContext callContext = context.createContext(createdBy, reason, comment, request);
final Account account = accountUserApi.getAccountById(UUID.fromString(accountId), callContext);
- final Iterable<InvoiceItemJson> sanitizedExternalChargesJson = cloneRefundItemsWithValidCurrency(account.getCurrency(), externalChargesJson);
+ final Iterable<InvoiceItem> sanitizedExternalChargesJson = validateSanitizeAndTranformInputItems(account.getCurrency(), externalChargesJson);
// Get the effective date of the external charge, in the account timezone
- final LocalDate requestedDate = toLocalDate(account, requestedDateTimeString, callContext);
-
- final Iterable<InvoiceItem> externalCharges = Iterables.<InvoiceItemJson, InvoiceItem>transform(sanitizedExternalChargesJson,
- new Function<InvoiceItemJson, InvoiceItem>() {
- @Override
- public InvoiceItem apply(final InvoiceItemJson invoiceItemJson) {
- return invoiceItemJson.toInvoiceItem();
- }
- }
- );
- final List<InvoiceItem> createdExternalCharges = invoiceApi.insertExternalCharges(account.getId(), requestedDate, externalCharges, autoCommit, callContext);
+ final LocalDate requestedDate = toLocalDateDefaultToday(account, requestedDateTimeString, callContext);
+ final List<InvoiceItem> createdExternalCharges = invoiceApi.insertExternalCharges(account.getId(), requestedDate, sanitizedExternalChargesJson, autoCommit, callContext);
if (payInvoice) {
final Collection<UUID> paidInvoices = new HashSet<UUID>();
@@ -508,9 +523,9 @@ public class InvoiceResource extends JaxRsResourceBase {
return Response.status(Status.OK).entity(createdExternalChargesJson).build();
}
- private Iterable<InvoiceItemJson> cloneRefundItemsWithValidCurrency(final Currency accountCurrency, final Iterable<InvoiceItemJson> inputItems) throws InvoiceApiException {
+ private Iterable<InvoiceItem> validateSanitizeAndTranformInputItems(final Currency accountCurrency, final Iterable<InvoiceItemJson> inputItems) throws InvoiceApiException {
try {
- return Iterables.transform(inputItems, new Function<InvoiceItemJson, InvoiceItemJson>() {
+ final Iterable<InvoiceItemJson> sanitized = Iterables.transform(inputItems, new Function<InvoiceItemJson, InvoiceItemJson>() {
@Override
public InvoiceItemJson apply(final InvoiceItemJson input) {
if (input.getCurrency() != null) {
@@ -521,7 +536,8 @@ public class InvoiceResource extends JaxRsResourceBase {
} else {
return new InvoiceItemJson(null,
input.getInvoiceId(),
- null, input.getAccountId(),
+ null,
+ input.getAccountId(),
input.getChildAccountId(),
input.getBundleId(),
input.getSubscriptionId(),
@@ -538,6 +554,13 @@ public class InvoiceResource extends JaxRsResourceBase {
}
}
});
+
+ return Iterables.transform(sanitized, new Function<InvoiceItemJson, InvoiceItem>() {
+ @Override
+ public InvoiceItem apply(final InvoiceItemJson input) {
+ return input.toInvoiceItem();
+ }
+ });
} catch (IllegalArgumentException e) {
throw new InvoiceApiException(ErrorCode.CURRENCY_INVALID, accountCurrency, e.getMessage());
}
@@ -973,13 +996,13 @@ public class InvoiceResource extends JaxRsResourceBase {
private final DryRunType dryRunType;
private final SubscriptionEventType action;
private final UUID subscriptionId;
- private final DateTime effectiveDate;
+ private final LocalDate effectiveDate;
private final PlanPhaseSpecifier specifier;
private final UUID bundleId;
private final BillingActionPolicy billingPolicy;
private final List<PlanPhasePriceOverride> overrides;
- public DefaultDryRunArguments(final InvoiceDryRunJson input, final DateTimeZone accountTimeZone, final Currency currency, final Clock clock) {
+ public DefaultDryRunArguments(final InvoiceDryRunJson input, final Account account) {
if (input == null) {
this.dryRunType = DryRunType.TARGET_DATE;
this.action = null;
@@ -994,7 +1017,7 @@ public class InvoiceResource extends JaxRsResourceBase {
this.action = input.getDryRunAction() != null ? SubscriptionEventType.valueOf(input.getDryRunAction()) : null;
this.subscriptionId = input.getSubscriptionId() != null ? UUID.fromString(input.getSubscriptionId()) : null;
this.bundleId = input.getBundleId() != null ? UUID.fromString(input.getBundleId()) : null;
- this.effectiveDate = input.getEffectiveDate() != null ? ClockUtil.computeDateTimeWithUTCReferenceTime(input.getEffectiveDate(), clock.getUTCNow().toLocalTime(), accountTimeZone, clock) : null;
+ this.effectiveDate = input.getEffectiveDate();
this.billingPolicy = input.getBillingPolicy() != null ? BillingActionPolicy.valueOf(input.getBillingPolicy()) : null;
final PlanPhaseSpecifier planPhaseSpecifier = (input.getProductName() != null &&
input.getProductCategory() != null &&
@@ -1012,9 +1035,9 @@ public class InvoiceResource extends JaxRsResourceBase {
@Override
public PlanPhasePriceOverride apply(@Nullable final PhasePriceOverrideJson input) {
if (input.getPhaseName() != null) {
- return new DefaultPlanPhasePriceOverride(input.getPhaseName(), currency, input.getFixedPrice(), input.getRecurringPrice());
+ return new DefaultPlanPhasePriceOverride(input.getPhaseName(), account.getCurrency(), input.getFixedPrice(), input.getRecurringPrice());
} else {
- return new DefaultPlanPhasePriceOverride(planPhaseSpecifier, currency, input.getFixedPrice(), input.getRecurringPrice());
+ return new DefaultPlanPhasePriceOverride(planPhaseSpecifier, account.getCurrency(), input.getFixedPrice(), input.getRecurringPrice());
}
}
})) : ImmutableList.<PlanPhasePriceOverride>of();
@@ -1042,7 +1065,7 @@ public class InvoiceResource extends JaxRsResourceBase {
}
@Override
- public DateTime getEffectiveDate() {
+ public LocalDate getEffectiveDate() {
return effectiveDate;
}
@@ -1061,5 +1084,4 @@ public class InvoiceResource extends JaxRsResourceBase {
return overrides;
}
}
-
}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
index d9de6ef..227b779 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
@@ -67,11 +67,14 @@ public interface JaxrsResource {
public static final String QUERY_EXTERNAL_KEY = "externalKey";
public static final String QUERY_API_KEY = "apiKey";
public static final String QUERY_REQUESTED_DT = "requestedDate";
+ public static final String QUERY_ENTITLEMENT_REQUESTED_DT = "entitlementDate";
+ public static final String QUERY_BILLING_REQUESTED_DT = "billingDate";
public static final String QUERY_CALL_COMPLETION = "callCompletion";
public static final String QUERY_USE_REQUESTED_DATE_FOR_BILLING = "useRequestedDateForBilling";
public static final String QUERY_CALL_TIMEOUT = "callTimeoutSec";
public static final String QUERY_TARGET_DATE = "targetDate";
public static final String QUERY_BILLING_POLICY = "billingPolicy";
+ public static final String QUERY_MIGRATED = "migrated";
public static final String QUERY_ENTITLEMENT_POLICY = "entitlementPolicy";
public static final String QUERY_SEARCH_OFFSET = "offset";
public static final String QUERY_SEARCH_LIMIT = "limit";
@@ -82,6 +85,7 @@ public interface JaxrsResource {
public static final String QUERY_ACCOUNT_ID = "accountId";
public static final String QUERY_INVOICE_WITH_ITEMS = "withItems";
+ public static final String QUERY_WITH_MIGRATION_INVOICES = "withMigrationInvoices";
public static final String QUERY_UNPAID_INVOICES_ONLY = "unpaidInvoicesOnly";
public static final String QUERY_PAYMENT_EXTERNAL = "externalPayment";
@@ -241,6 +245,7 @@ public interface JaxrsResource {
public static final String COMMIT_INVOICE = "commitInvoice";
public static final String COMBO = "combo";
+ public static final String MIGRATION = "migration";
public static final String CHILDREN = "children";
public static final String CHILDREN_PATH = PREFIX + "/" + CHILDREN;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
index 6292294..82cc906 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
@@ -30,6 +30,7 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
import javax.annotation.Nullable;
@@ -39,8 +40,6 @@ import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.StreamingOutput;
import javax.ws.rs.core.UriInfo;
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
@@ -91,6 +90,7 @@ import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
+import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
@@ -298,51 +298,19 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
}
}
- protected LocalDate toLocalDate(final UUID accountId, final String inputDate, final TenantContext context) throws AccountApiException {
- final LocalDate maybeResult = extractLocalDate(inputDate);
- if (maybeResult != null) {
- return maybeResult;
- }
- Account account = accountId != null ? accountUserApi.getAccountById(accountId, context) : null;
- final DateTime inputDateTime = inputDate != null ? DATE_TIME_FORMATTER.parseDateTime(inputDate) : clock.getUTCNow();
- return toLocalDate(account, inputDateTime, context);
+ protected LocalDate toLocalDateDefaultToday(final UUID accountId, @Nullable final String inputDate, final TenantContext context) throws AccountApiException {
+ final Account account = accountId != null ? accountUserApi.getAccountById(accountId, context) : null;
+ return toLocalDateDefaultToday(account, inputDate, context);
}
- protected LocalDate toLocalDate(final Account account, final String inputDate, final TenantContext context) {
-
- final LocalDate maybeResult = extractLocalDate(inputDate);
- if (maybeResult != null) {
- return maybeResult;
- }
- final DateTime inputDateTime = inputDate != null ? DATE_TIME_FORMATTER.parseDateTime(inputDate) : clock.getUTCNow();
- return toLocalDate(account, inputDateTime, context);
+ protected LocalDate toLocalDateDefaultToday(final Account account, @Nullable final String inputDate, final TenantContext context) {
+ // TODO Switch to cached normalized timezone when available
+ return MoreObjects.firstNonNull(toLocalDate(inputDate, context), clock.getToday(account.getTimeZone()));
}
- private LocalDate toLocalDate(final Account account, final DateTime inputDate, final TenantContext context) {
- if (account == null && inputDate == null) {
- // We have no inputDate and so accountTimeZone so we default to LocalDate as seen in UTC
- return new LocalDate(clock.getUTCNow(), DateTimeZone.UTC);
- } else if (account == null && inputDate != null) {
- // We were given a date but can't get timezone, default in UTC
- return new LocalDate(inputDate, DateTimeZone.UTC);
- } else if (account != null && inputDate == null) {
- // We have no inputDate but for accountTimeZone so default to LocalDate as seen in account timezone
- return new LocalDate(clock.getUTCNow(), account.getTimeZone());
- } else {
- // Precise LocalDate as requested
- return new LocalDate(inputDate, account.getTimeZone());
- }
- }
-
- private LocalDate extractLocalDate(final String inputDate) {
- if (inputDate != null) {
- try {
- final LocalDate localDate = LocalDate.parse(inputDate, LOCAL_DATE_FORMATTER);
- return localDate;
- } catch (final IllegalArgumentException expectedAndIgnore) {
- }
- }
- return null;
+ // API for subscription and invoice generation: keep null, the lower layers will default to now()
+ protected LocalDate toLocalDate(@Nullable final String inputDate, final TenantContext context) {
+ return inputDate == null ? null : LocalDate.parse(inputDate, LOCAL_DATE_FORMATTER);
}
protected Iterable<PluginProperty> extractPluginProperties(@Nullable final Iterable<PluginPropertyJson> pluginProperties) {
@@ -475,4 +443,9 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
Preconditions.checkArgument(actual == expected, errorMessage);
}
+ protected void logDeprecationParameterWarningIfNeeded(@Nullable final String deprecatedParam, final String...replacementParams) {
+ if (deprecatedParam != null) {
+ log.warn(String.format("Parameter %s is being deprecated: Instead use parameters %s", deprecatedParam, Joiner.on(",").join(replacementParams)));
+ }
+ }
}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
index 79968cc..57dd579 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
@@ -124,7 +124,7 @@ public class PaymentResource extends ComboPaymentResource {
@TimedResource(name = "getPayment")
@GET
@Produces(APPLICATION_JSON)
- @ApiOperation(value = "Retrieve a payment by id", response = PaymentJson.class)
+ @ApiOperation(value = "Retrieve a payment by external key", response = PaymentJson.class)
@ApiResponses(value = {@ApiResponse(code = 404, message = "Payment not found")})
public Response getPaymentByExternalKey(@QueryParam(QUERY_WITH_PLUGIN_INFO) @DefaultValue("false") final Boolean withPluginInfo,
@QueryParam(QUERY_EXTERNAL_KEY) final String paymentExternalKey,
@@ -383,13 +383,14 @@ public class PaymentResource extends ComboPaymentResource {
@ApiResponse(code = 404, message = "Account or payment not found")})
public Response captureAuthorization(final PaymentTransactionJson json,
@PathParam("paymentId") final String paymentIdStr,
+ @QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
@HeaderParam(HDR_CREATED_BY) final String createdBy,
@HeaderParam(HDR_REASON) final String reason,
@HeaderParam(HDR_COMMENT) final String comment,
@javax.ws.rs.core.Context final UriInfo uriInfo,
@javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
- return captureAuthorizationInternal(json, paymentIdStr, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
+ return captureAuthorizationInternal(json, paymentIdStr, paymentControlPluginNames, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
}
@TimedResource(name = "captureAuthorization")
@@ -399,17 +400,19 @@ public class PaymentResource extends ComboPaymentResource {
@ApiOperation(value = "Capture an existing authorization")
@ApiResponses(value = {@ApiResponse(code = 404, message = "Account or payment not found")})
public Response captureAuthorizationByExternalKey(final PaymentTransactionJson json,
+ @QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
@HeaderParam(HDR_CREATED_BY) final String createdBy,
@HeaderParam(HDR_REASON) final String reason,
@HeaderParam(HDR_COMMENT) final String comment,
@javax.ws.rs.core.Context final UriInfo uriInfo,
@javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
- return captureAuthorizationInternal(json, null, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
+ return captureAuthorizationInternal(json, null, paymentControlPluginNames, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
}
private Response captureAuthorizationInternal(final PaymentTransactionJson json,
@Nullable final String paymentIdStr,
+ final List<String> paymentControlPluginNames,
final List<String> pluginPropertiesString,
final String createdBy,
final String reason,
@@ -427,8 +430,10 @@ public class PaymentResource extends ComboPaymentResource {
final Account account = accountUserApi.getAccountById(initialPayment.getAccountId(), callContext);
final Currency currency = json.getCurrency() == null ? account.getCurrency() : Currency.valueOf(json.getCurrency());
- final Payment payment = paymentApi.createCapture(account, initialPayment.getId(), json.getAmount(), currency,
- json.getTransactionExternalKey(), pluginProperties, callContext);
+ final PaymentOptions paymentOptions = createControlPluginApiPaymentOptions(paymentControlPluginNames);
+
+ final Payment payment = paymentApi.createCaptureWithPaymentControl(account, initialPayment.getId(), json.getAmount(), currency,
+ json.getTransactionExternalKey(), pluginProperties, paymentOptions, callContext);
return uriBuilder.buildResponse(uriInfo, PaymentResource.class, "getPayment", payment.getId());
}
@@ -442,13 +447,14 @@ public class PaymentResource extends ComboPaymentResource {
@ApiResponse(code = 404, message = "Account or payment not found")})
public Response refundPayment(final PaymentTransactionJson json,
@PathParam("paymentId") final String paymentIdStr,
+ @QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
@HeaderParam(HDR_CREATED_BY) final String createdBy,
@HeaderParam(HDR_REASON) final String reason,
@HeaderParam(HDR_COMMENT) final String comment,
@javax.ws.rs.core.Context final UriInfo uriInfo,
@javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
- return refundPaymentInternal(json, paymentIdStr, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
+ return refundPaymentInternal(json, paymentIdStr, paymentControlPluginNames, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
}
@TimedResource(name = "refundPayment")
@@ -459,18 +465,20 @@ public class PaymentResource extends ComboPaymentResource {
@ApiOperation(value = "Refund an existing payment")
@ApiResponses(value = {@ApiResponse(code = 404, message = "Account or payment not found")})
public Response refundPaymentByExternalKey(final PaymentTransactionJson json,
+ @QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
@HeaderParam(HDR_CREATED_BY) final String createdBy,
@HeaderParam(HDR_REASON) final String reason,
@HeaderParam(HDR_COMMENT) final String comment,
@javax.ws.rs.core.Context final UriInfo uriInfo,
@javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
- return refundPaymentInternal(json, null, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
+ return refundPaymentInternal(json, null, paymentControlPluginNames, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
}
private Response refundPaymentInternal(final PaymentTransactionJson json,
@Nullable final String paymentIdStr,
+ final List<String> paymentControlPluginNames,
final List<String> pluginPropertiesString,
final String createdBy,
final String reason,
@@ -488,8 +496,10 @@ public class PaymentResource extends ComboPaymentResource {
final Account account = accountUserApi.getAccountById(initialPayment.getAccountId(), callContext);
final Currency currency = json.getCurrency() == null ? account.getCurrency() : Currency.valueOf(json.getCurrency());
- final Payment payment = paymentApi.createRefund(account, initialPayment.getId(), json.getAmount(), currency,
- json.getTransactionExternalKey(), pluginProperties, callContext);
+ final PaymentOptions paymentOptions = createControlPluginApiPaymentOptions(paymentControlPluginNames);
+
+ final Payment payment = paymentApi.createRefundWithPaymentControl(account, initialPayment.getId(), json.getAmount(), currency,
+ json.getTransactionExternalKey(), pluginProperties, paymentOptions, callContext);
return uriBuilder.buildResponse(uriInfo, PaymentResource.class, "getPayment", payment.getId());
}
@@ -504,13 +514,14 @@ public class PaymentResource extends ComboPaymentResource {
@ApiResponse(code = 404, message = "Account or payment not found")})
public Response voidPayment(final PaymentTransactionJson json,
@PathParam("paymentId") final String paymentIdStr,
+ @QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
@HeaderParam(HDR_CREATED_BY) final String createdBy,
@HeaderParam(HDR_REASON) final String reason,
@HeaderParam(HDR_COMMENT) final String comment,
@javax.ws.rs.core.Context final UriInfo uriInfo,
@javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
- return voidPaymentInternal(json, paymentIdStr, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
+ return voidPaymentInternal(json, paymentIdStr, paymentControlPluginNames, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
}
@TimedResource(name = "voidPayment")
@@ -520,17 +531,19 @@ public class PaymentResource extends ComboPaymentResource {
@ApiOperation(value = "Void an existing payment")
@ApiResponses(value = {@ApiResponse(code = 404, message = "Account or payment not found")})
public Response voidPaymentByExternalKey(final PaymentTransactionJson json,
+ @QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
@HeaderParam(HDR_CREATED_BY) final String createdBy,
@HeaderParam(HDR_REASON) final String reason,
@HeaderParam(HDR_COMMENT) final String comment,
@javax.ws.rs.core.Context final UriInfo uriInfo,
@javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
- return voidPaymentInternal(json, null, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
+ return voidPaymentInternal(json, null, paymentControlPluginNames, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
}
private Response voidPaymentInternal(final PaymentTransactionJson json,
@Nullable final String paymentIdStr,
+ final List<String> paymentControlPluginNames,
final List<String> pluginPropertiesString,
final String createdBy,
final String reason,
@@ -544,7 +557,10 @@ public class PaymentResource extends ComboPaymentResource {
final Account account = accountUserApi.getAccountById(initialPayment.getAccountId(), callContext);
final String transactionExternalKey = json != null ? json.getTransactionExternalKey() : null;
- final Payment payment = paymentApi.createVoid(account, initialPayment.getId(), transactionExternalKey, pluginProperties, callContext);
+ final PaymentOptions paymentOptions = createControlPluginApiPaymentOptions(paymentControlPluginNames);
+
+ final Payment payment = paymentApi.createVoidWithPaymentControl(account, initialPayment.getId(), transactionExternalKey,
+ pluginProperties, paymentOptions, callContext);
return uriBuilder.buildResponse(uriInfo, PaymentResource.class, "getPayment", payment.getId());
}
@@ -558,13 +574,14 @@ public class PaymentResource extends ComboPaymentResource {
@ApiResponse(code = 404, message = "Account not found")})
public Response chargebackPayment(final PaymentTransactionJson json,
@PathParam("paymentId") final String paymentIdStr,
+ @QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
@HeaderParam(HDR_CREATED_BY) final String createdBy,
@HeaderParam(HDR_REASON) final String reason,
@HeaderParam(HDR_COMMENT) final String comment,
@javax.ws.rs.core.Context final UriInfo uriInfo,
@javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
- return chargebackPaymentInternal(json, paymentIdStr, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
+ return chargebackPaymentInternal(json, paymentIdStr, paymentControlPluginNames, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
}
@TimedResource(name = "chargebackPayment")
@@ -575,17 +592,19 @@ public class PaymentResource extends ComboPaymentResource {
@ApiOperation(value = "Record a chargeback")
@ApiResponses(value = {@ApiResponse(code = 404, message = "Account not found")})
public Response chargebackPaymentByExternalKey(final PaymentTransactionJson json,
+ @QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
@HeaderParam(HDR_CREATED_BY) final String createdBy,
@HeaderParam(HDR_REASON) final String reason,
@HeaderParam(HDR_COMMENT) final String comment,
@javax.ws.rs.core.Context final UriInfo uriInfo,
@javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
- return chargebackPaymentInternal(json, null, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
+ return chargebackPaymentInternal(json, null, paymentControlPluginNames, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
}
private Response chargebackPaymentInternal(final PaymentTransactionJson json,
@Nullable final String paymentIdStr,
+ final List<String> paymentControlPluginNames,
final List<String> pluginPropertiesString,
final String createdBy,
final String reason,
@@ -602,8 +621,10 @@ public class PaymentResource extends ComboPaymentResource {
final Account account = accountUserApi.getAccountById(initialPayment.getAccountId(), callContext);
final Currency currency = json.getCurrency() == null ? account.getCurrency() : Currency.valueOf(json.getCurrency());
- final Payment payment = paymentApi.createChargeback(account, initialPayment.getId(), json.getAmount(), currency,
- json.getTransactionExternalKey(), callContext);
+ final PaymentOptions paymentOptions = createControlPluginApiPaymentOptions(paymentControlPluginNames);
+
+ final Payment payment = paymentApi.createChargebackWithPaymentControl(account, initialPayment.getId(), json.getAmount(), currency,
+ json.getTransactionExternalKey(), paymentOptions, callContext);
return uriBuilder.buildResponse(uriInfo, PaymentResource.class, "getPayment", payment.getId());
}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
index 48d2446..d5662dd 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
@@ -47,6 +47,7 @@ import org.killbill.billing.account.api.AccountApiException;
import org.killbill.billing.account.api.AccountUserApi;
import org.killbill.billing.catalog.api.BillingActionPolicy;
import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.PhaseType;
import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
import org.killbill.billing.catalog.api.PlanSpecifier;
@@ -60,6 +61,7 @@ import org.killbill.billing.entitlement.api.Subscription;
import org.killbill.billing.entitlement.api.SubscriptionApi;
import org.killbill.billing.entitlement.api.SubscriptionApiException;
import org.killbill.billing.entitlement.api.SubscriptionBundle;
+import org.killbill.billing.events.BlockingTransitionInternalEvent;
import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
import org.killbill.billing.events.InvoiceCreationInternalEvent;
import org.killbill.billing.events.NullInvoiceInternalEvent;
@@ -91,7 +93,6 @@ import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
-import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.inject.Inject;
import com.wordnik.swagger.annotations.Api;
@@ -152,7 +153,10 @@ public class SubscriptionResource extends JaxRsResourceBase {
@ApiOperation(value = "Create an entitlement")
@ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid entitlement supplied")})
public Response createEntitlement(final SubscriptionJson entitlement,
- @QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
+ @QueryParam(QUERY_REQUESTED_DT) final String requestedDate, /* This is deprecated, only used for backward compatibility */
+ @QueryParam(QUERY_ENTITLEMENT_REQUESTED_DT) final String entitlementDate,
+ @QueryParam(QUERY_BILLING_REQUESTED_DT) final String billingDate,
+ @QueryParam(QUERY_MIGRATED) @DefaultValue("false") final Boolean isMigrated,
@QueryParam(QUERY_CALL_COMPLETION) @DefaultValue("false") final Boolean callCompletion,
@QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("3") final long timeoutSec,
@QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
@@ -166,6 +170,9 @@ public class SubscriptionResource extends JaxRsResourceBase {
entitlement.getProductCategory(), "SubscriptionJson productCategory needs to be set",
entitlement.getBillingPeriod(), "SubscriptionJson billingPeriod needs to be set",
entitlement.getPriceList(), "SubscriptionJson priceList needs to be set");
+
+ logDeprecationParameterWarningIfNeeded(QUERY_REQUESTED_DT, QUERY_ENTITLEMENT_REQUESTED_DT, QUERY_BILLING_REQUESTED_DT);
+
// For ADD_ON we can provide externalKey or the bundleId
final boolean createAddOnEntitlement = ProductCategory.ADD_ON.toString().equals(entitlement.getProductCategory());
if (createAddOnEntitlement) {
@@ -181,19 +188,21 @@ public class SubscriptionResource extends JaxRsResourceBase {
public Entitlement doOperation(final CallContext ctx) throws InterruptedException, TimeoutException, EntitlementApiException, SubscriptionApiException, AccountApiException {
final Account account = getAccountFromSubscriptionJson(entitlement, callContext);
+ final PhaseType phaseType = entitlement.getPhaseType() != null ? PhaseType.valueOf(entitlement.getPhaseType()) : null;
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(entitlement.getProductName(),
ProductCategory.valueOf(entitlement.getProductCategory()),
- BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList(), null);
+ BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList(), phaseType);
- final LocalDate inputLocalDate = toLocalDate(account, requestedDate, callContext);
+ final LocalDate resolvedEntitlementDate = requestedDate != null ? toLocalDate(requestedDate, callContext) : toLocalDate(entitlementDate, callContext);
+ final LocalDate resolvedBillingDate = requestedDate != null ? toLocalDate(requestedDate, callContext) : toLocalDate(billingDate, callContext);
final PlanSpecifier planSpec = new PlanSpecifier(entitlement.getProductName(),
ProductCategory.valueOf(entitlement.getProductCategory()),
BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList());
final List<PlanPhasePriceOverride> overrides = PhasePriceOverrideJson.toPlanPhasePriceOverrides(entitlement.getPriceOverrides(), planSpec, account.getCurrency());
return createAddOnEntitlement ?
- entitlementApi.addEntitlement(getBundleIdForAddOnCreation(entitlement), spec, overrides, inputLocalDate, pluginProperties, callContext) :
- entitlementApi.createBaseEntitlement(account.getId(), spec, entitlement.getExternalKey(), overrides, inputLocalDate, pluginProperties, callContext);
+ entitlementApi.addEntitlement(getBundleIdForAddOnCreation(entitlement), spec, overrides, resolvedEntitlementDate, resolvedBillingDate, isMigrated, pluginProperties, callContext) :
+ entitlementApi.createBaseEntitlement(account.getId(), spec, entitlement.getExternalKey(), overrides, resolvedEntitlementDate, resolvedBillingDate, isMigrated, pluginProperties, callContext);
}
private UUID getBundleIdForAddOnCreation(final SubscriptionJson entitlement) throws SubscriptionApiException {
@@ -229,15 +238,18 @@ public class SubscriptionResource extends JaxRsResourceBase {
@ApiOperation(value = "Create an entitlement with addOn products")
@ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid entitlement supplied")})
public Response createEntitlementWithAddOns(final List<SubscriptionJson> entitlements,
- @QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
- @QueryParam(QUERY_CALL_COMPLETION) @DefaultValue("false") final Boolean callCompletion,
- @QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("3") final long timeoutSec,
- @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
- @HeaderParam(HDR_CREATED_BY) final String createdBy,
- @HeaderParam(HDR_REASON) final String reason,
- @HeaderParam(HDR_COMMENT) final String comment,
- @javax.ws.rs.core.Context final HttpServletRequest request,
- @javax.ws.rs.core.Context final UriInfo uriInfo) throws EntitlementApiException, AccountApiException, SubscriptionApiException {
+ @QueryParam(QUERY_REQUESTED_DT) final String requestedDate, /* This is deprecated, only used for backward compatibility */
+ @QueryParam(QUERY_ENTITLEMENT_REQUESTED_DT) final String entitlementDate,
+ @QueryParam(QUERY_BILLING_REQUESTED_DT) final String billingDate,
+ @QueryParam(QUERY_MIGRATED) @DefaultValue("false") final Boolean isMigrated,
+ @QueryParam(QUERY_CALL_COMPLETION) @DefaultValue("false") final Boolean callCompletion,
+ @QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("3") final long timeoutSec,
+ @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
+ @HeaderParam(HDR_CREATED_BY) final String createdBy,
+ @HeaderParam(HDR_REASON) final String reason,
+ @HeaderParam(HDR_COMMENT) final String comment,
+ @javax.ws.rs.core.Context final HttpServletRequest request,
+ @javax.ws.rs.core.Context final UriInfo uriInfo) throws EntitlementApiException, AccountApiException, SubscriptionApiException {
Preconditions.checkArgument(Iterables.size(entitlements) > 0, "Subscription list mustn't be null or empty.");
@@ -249,6 +261,8 @@ public class SubscriptionResource extends JaxRsResourceBase {
entitlement.getPriceList(), "SubscriptionJson priceList needs to be set for each element");
}
+ logDeprecationParameterWarningIfNeeded(QUERY_REQUESTED_DT, QUERY_ENTITLEMENT_REQUESTED_DT, QUERY_BILLING_REQUESTED_DT);
+
final int baseSubscriptionsSize = Iterables.size(Iterables.filter(entitlements, new Predicate<SubscriptionJson>() {
@Override
public boolean apply(final SubscriptionJson subscription) {
@@ -312,9 +326,10 @@ public class SubscriptionResource extends JaxRsResourceBase {
entitlementSpecifierList.add(specifier);
}
- final LocalDate inputLocalDate = toLocalDate(account, requestedDate, callContext);
+ final LocalDate resolvedEntitlementDate = requestedDate != null ? toLocalDate(requestedDate, callContext) : toLocalDate(entitlementDate, callContext);
+ final LocalDate resolvedBillingDate = requestedDate != null ? toLocalDate(requestedDate, callContext) : toLocalDate(billingDate, callContext);
return entitlementApi.createBaseEntitlementWithAddOns(account.getId(), baseEntitlement.getExternalKey(), entitlementSpecifierList,
- inputLocalDate, pluginProperties, callContext);
+ resolvedEntitlementDate, resolvedBillingDate, isMigrated, pluginProperties, callContext);
}
@Override
@@ -390,7 +405,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
final UUID uuid = UUID.fromString(subscriptionId);
final Entitlement current = entitlementApi.getEntitlementForId(uuid, callContext);
- final LocalDate inputLocalDate = toLocalDate(current.getAccountId(), requestedDate, callContext);
+ final LocalDate inputLocalDate = toLocalDate(requestedDate, callContext);
final Entitlement newEntitlement;
final Account account = accountUserApi.getAccountById(current.getAccountId(), callContext);
@@ -465,7 +480,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
final UUID uuid = UUID.fromString(subscriptionId);
final Entitlement current = entitlementApi.getEntitlementForId(uuid, ctx);
- final LocalDate inputLocalDate = toLocalDate(current.getAccountId(), requestedDate, callContext);
+ final LocalDate inputLocalDate = toLocalDate(requestedDate, callContext);
final Entitlement newEntitlement;
if (billingPolicyString == null && entitlementPolicyString == null) {
newEntitlement = current.cancelEntitlementWithDate(inputLocalDate, useRequestedDateForBilling, pluginProperties, ctx);
@@ -512,12 +527,16 @@ public class SubscriptionResource extends JaxRsResourceBase {
@Override
public void onSubscriptionBaseTransition(final EffectiveSubscriptionInternalEvent event) {
-
log.info(String.format("Got event SubscriptionBaseTransition token = %s, type = %s, remaining = %d ",
event.getUserToken(), event.getTransitionType(), event.getRemainingEventsForUserOperation()));
}
@Override
+ public void onBlockingState(final BlockingTransitionInternalEvent event) {
+ log.info(String.format("Got event BlockingTransitionInternalEvent token = %s", event.getUserToken()));
+ }
+
+ @Override
public void onEmptyInvoice(final NullInvoiceInternalEvent event) {
log.info(String.format("Got event EmptyInvoiceNotification token = %s ", event.getUserToken()));
notifyForCompletion();
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
index c11ff57..b55db89 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
@@ -130,7 +130,6 @@ public class UsageResource extends JaxRsResourceBase {
@QueryParam(QUERY_START_DATE) final String startDate,
@QueryParam(QUERY_END_DATE) final String endDate,
@javax.ws.rs.core.Context final HttpServletRequest request) {
-
if (startDate == null || endDate == null) {
return Response.status(Status.BAD_REQUEST).build();
}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/KillbillEventHandler.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/KillbillEventHandler.java
index 1b4a1d4..68e8875 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/KillbillEventHandler.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/KillbillEventHandler.java
@@ -58,7 +58,7 @@ public class KillbillEventHandler {
*/
@AllowConcurrentEvents
@Subscribe
- public void handleSubscriptionevents(final BusInternalEvent event) {
+ public void handleSubscriptionEvents(final BusInternalEvent event) {
final List<CompletionUserRequestNotifier> runningWaiters = new ArrayList<CompletionUserRequestNotifier>();
synchronized (activeWaiters) {
runningWaiters.addAll(activeWaiters);
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/glue/TestJaxrsModuleNoDB.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/glue/TestJaxrsModuleNoDB.java
index fa2dd35..559d3d4 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/glue/TestJaxrsModuleNoDB.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/glue/TestJaxrsModuleNoDB.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -18,8 +18,14 @@
package org.killbill.billing.jaxrs.glue;
+import org.apache.shiro.mgt.SecurityManager;
import org.killbill.billing.GuicyKillbillTestNoDBModule;
+import org.killbill.billing.mock.glue.MockAccountModule;
+import org.killbill.billing.mock.glue.MockNonEntityDaoModule;
import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.tenant.api.TenantInternalApi;
+import org.killbill.billing.util.glue.CacheModule;
+import org.mockito.Mockito;
public class TestJaxrsModuleNoDB extends TestJaxrsModule {
@@ -31,5 +37,11 @@ public class TestJaxrsModuleNoDB extends TestJaxrsModule {
public void configure() {
super.configure();
install(new GuicyKillbillTestNoDBModule(configSource));
+
+ install(new MockNonEntityDaoModule(configSource));
+ install(new MockAccountModule(configSource));
+ install(new CacheModule(configSource));
+ bind(TenantInternalApi.class).toInstance(Mockito.mock(TenantInternalApi.class));
+ bind(SecurityManager.class).toInstance(Mockito.mock(SecurityManager.class));
}
}
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestBundleJsonWithSubscriptions.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestBundleJsonWithSubscriptions.java
index 201c5fc..c27054c 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestBundleJsonWithSubscriptions.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestBundleJsonWithSubscriptions.java
@@ -45,7 +45,6 @@ public class TestBundleJsonWithSubscriptions extends JaxrsTestSuiteNoDB {
final EventSubscriptionJson event = new EventSubscriptionJson(UUID.randomUUID().toString(),
BillingPeriod.NO_BILLING_PERIOD.toString(),
new LocalDate(),
- new LocalDate(),
UUID.randomUUID().toString(),
UUID.randomUUID().toString(),
UUID.randomUUID().toString(),
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestBundleTimelineJson.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestBundleTimelineJson.java
index 9874b55..5749e61 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestBundleTimelineJson.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestBundleTimelineJson.java
@@ -36,7 +36,6 @@ public class TestBundleTimelineJson extends JaxrsTestSuiteNoDB {
final EventSubscriptionJson event = new EventSubscriptionJson(UUID.randomUUID().toString(),
BillingPeriod.NO_BILLING_PERIOD.toString(),
new LocalDate(),
- new LocalDate(),
UUID.randomUUID().toString(),
UUID.randomUUID().toString(),
UUID.randomUUID().toString(),
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestCreditJson.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestCreditJson.java
index c86a648..e698ec6 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestCreditJson.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestCreditJson.java
@@ -40,7 +40,7 @@ public class TestCreditJson extends JaxrsTestSuiteNoDB {
final String accountId = UUID.randomUUID().toString();
final List<AuditLogJson> auditLogs = createAuditLogsJson(clock.getUTCNow());
final CreditJson creditJson = new CreditJson(creditAmount, invoiceId, invoiceNumber, effectiveDate,
- accountId, auditLogs);
+ accountId, null, auditLogs);
Assert.assertEquals(creditJson.getEffectiveDate(), effectiveDate);
Assert.assertEquals(creditJson.getCreditAmount(), creditAmount);
Assert.assertEquals(creditJson.getInvoiceId(), invoiceId);
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestEntitlementJsonWithEvents.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestEntitlementJsonWithEvents.java
index 6026c77..41ed2ba 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestEntitlementJsonWithEvents.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestEntitlementJsonWithEvents.java
@@ -46,13 +46,11 @@ public class TestEntitlementJsonWithEvents extends JaxrsTestSuiteNoDB {
final String bundleId = UUID.randomUUID().toString();
final String subscriptionId = UUID.randomUUID().toString();
final String externalKey = UUID.randomUUID().toString();
- final DateTime requestedDate = DefaultClock.toUTCDateTime(new DateTime(DateTimeZone.UTC));
final DateTime effectiveDate = DefaultClock.toUTCDateTime(new DateTime(DateTimeZone.UTC));
final UUID eventId = UUID.randomUUID();
final List<AuditLogJson> auditLogs = createAuditLogsJson(clock.getUTCNow());
final EventSubscriptionJson newEvent = new EventSubscriptionJson(eventId.toString(),
BillingPeriod.NO_BILLING_PERIOD.toString(),
- requestedDate.toLocalDate(),
effectiveDate.toLocalDate(),
UUID.randomUUID().toString(),
UUID.randomUUID().toString(),
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceJsonWithBundleKeys.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceJsonWithBundleKeys.java
index e5e369e..0fb96af 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceJsonWithBundleKeys.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceJsonWithBundleKeys.java
@@ -116,6 +116,6 @@ public class TestInvoiceJsonWithBundleKeys extends JaxrsTestSuiteNoDB {
final String invoiceNumber = UUID.randomUUID().toString();
final LocalDate effectiveDate = clock.getUTCToday();
final String accountId = UUID.randomUUID().toString();
- return new CreditJson(creditAmount, invoiceId, invoiceNumber, effectiveDate, accountId, null);
+ return new CreditJson(creditAmount, invoiceId, invoiceNumber, effectiveDate, accountId, null, null);
}
}
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/TestDateConversion.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/TestDateConversion.java
index a09bf3f..a754a75 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/TestDateConversion.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/TestDateConversion.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -55,22 +57,18 @@ public class TestDateConversion extends JaxRsResourceBase {
//
// BASIC Tests to understand how toLocalDate converts different inputs (null, LocalDate, DateTime)
//
- @Test(groups = "fast")
- public void testDateTimeConversion() throws AccountApiException {
- final UUID accountId = setupAccount(DateTimeZone.forOffsetHours(-8));
- final String input = "2013-08-26T06:50:20Z";
- final LocalDate result = toLocalDate(accountId, input, null);
- Assert.assertTrue(result.compareTo(new LocalDate(2013, 8, 25)) == 0);
- }
-
@Test(groups = "fast")
public void testNullConversion() throws AccountApiException {
+ final String input = null;
+
+ final LocalDate result = toLocalDate(input, null);
+ Assert.assertNull(result);
+
final UUID accountId = setupAccount(DateTimeZone.forOffsetHours(-8));
((ClockMock) clock).setTime(new DateTime("2013-08-26T06:50:20Z"));
- final String input = null;
- final LocalDate result = toLocalDate(accountId, input, null);
- Assert.assertTrue(result.compareTo(new LocalDate(2013, 8, 25)) == 0);
+ final LocalDate result2 = toLocalDateDefaultToday(accountId, input, null);
+ Assert.assertTrue(result2.compareTo(new LocalDate(2013, 8, 25)) == 0);
((ClockMock) clock).resetDeltaFromReality();
}
@@ -78,7 +76,7 @@ public class TestDateConversion extends JaxRsResourceBase {
public void testLocalDateConversion() throws AccountApiException {
final UUID accountId = setupAccount(DateTimeZone.forOffsetHours(-8));
final String input = "2013-08-25";
- final LocalDate result = toLocalDate(accountId, input, null);
+ final LocalDate result = toLocalDate(input, null);
Assert.assertTrue(result.compareTo(new LocalDate(2013, 8, 25)) == 0);
}
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java
index df27149..4cee36a 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -40,6 +42,7 @@ import org.killbill.billing.subscription.api.SubscriptionBase;
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.clock.ClockUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -123,7 +126,7 @@ public class BillCycleDayCalculator {
// TODO - this should be extracted somewhere, along with this code above
final PhaseType initialPhaseType;
final List<EffectiveSubscriptionInternalEvent> transitions = subscriptionApi.getAllTransitions(subscription, context);
- if (transitions.size() == 0) {
+ if (transitions.isEmpty()) {
initialPhaseType = null;
} else {
final DateTime requestedDate = subscription.getStartDate();
@@ -141,10 +144,9 @@ public class BillCycleDayCalculator {
}
final DateTime date = plan.dateOfFirstRecurringNonZeroCharge(subscription.getStartDate(), initialPhaseType);
- final int bcdUTC = date.toDateTime(DateTimeZone.UTC).getDayOfMonth();
- final int bcdLocal = date.toDateTime(account.getTimeZone()).getDayOfMonth();
- log.info("Calculated BCD: subscription id {}, subscription start {}, timezone {}, bcd UTC {}, bcd local {}",
- subscription.getId(), date.toDateTimeISO(), account.getTimeZone(), bcdUTC, bcdLocal);
+ final int bcdLocal = ClockUtil.toDateTime(date, account.getTimeZone()).getDayOfMonth();
+ log.info("Calculated BCD: subscription id {}, subscription start {}, timezone {}, bcd {}",
+ subscription.getId(), date.toDateTimeISO(), account.getTimeZone(), bcdLocal);
return bcdLocal;
}
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
index 3e36378..b43bd54 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
@@ -1,7 +1,8 @@
/*
- * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -33,8 +34,7 @@ import javax.annotation.Nullable;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
-
-import org.killbill.billing.account.api.Account;
+import org.joda.time.Days;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Currency;
@@ -103,7 +103,6 @@ public class BlockingCalculator {
final SortedSet<BillingEvent> billingEventsToAdd = new TreeSet<BillingEvent>();
final SortedSet<BillingEvent> billingEventsToRemove = new TreeSet<BillingEvent>();
-
final List<BlockingState> blockingEvents = blockingApi.getBlockingAllForAccount(context);
final Iterable<BlockingState> accountBlockingEvents = Iterables.filter(blockingEvents, new Predicate<BlockingState>() {
@@ -118,7 +117,7 @@ public class BlockingCalculator {
for (final UUID bundleId : bundleMap.keySet()) {
- final List<BlockingState> bundleBlockingEvents = perBundleBlockingEvents.get(bundleId) != null ? perBundleBlockingEvents.get(bundleId) : ImmutableList.<BlockingState>of();
+ final List<BlockingState> bundleBlockingEvents = perBundleBlockingEvents.get(bundleId) != null ? perBundleBlockingEvents.get(bundleId) : ImmutableList.<BlockingState>of();
for (final SubscriptionBase subscription : bundleMap.get(bundleId)) {
// Avoid inserting additional events for subscriptions that don't even have a START event
@@ -144,10 +143,9 @@ public class BlockingCalculator {
}
}
-
final List<BlockingState> getAggregateBlockingEventsPerSubscription(final Iterable<BlockingState> subscriptionBlockingEvents, final Iterable<BlockingState> bundleBlockingEvents, final Iterable<BlockingState> accountBlockingEvents) {
final Iterable<BlockingState> tmp = Iterables.concat(subscriptionBlockingEvents, bundleBlockingEvents, accountBlockingEvents);
- final List<BlockingState> result = Lists.newArrayList(tmp);
+ final List<BlockingState> result = Lists.newArrayList(tmp);
Collections.sort(result);
return result;
}
@@ -161,7 +159,7 @@ public class BlockingCalculator {
});
final Map<UUID, List<BlockingState>> perTypeBlockingEvents = new HashMap<UUID, List<BlockingState>>();
- for (final BlockingState cur : bundleBlockingEvents) {
+ for (final BlockingState cur : bundleBlockingEvents) {
if (!perTypeBlockingEvents.containsKey(cur.getBlockedId())) {
perTypeBlockingEvents.put(cur.getBlockedId(), new ArrayList<BlockingState>());
}
@@ -352,7 +350,7 @@ public class BlockingCalculator {
final DateTime endDate = firstNonBlocking == null ? null : firstNonBlocking.getEffectiveDate();
if (lastOne != null && lastOne.getEnd().compareTo(startDate) == 0) {
lastOne.setEnd(endDate);
- } else if (endDate == null || startDate.toLocalDate().compareTo(endDate.toLocalDate()) != 0) {
+ } else if (endDate == null || Days.daysBetween(startDate, endDate).getDays() >= 1) {
// Don't disable for periods less than a day (see https://github.com/killbill/killbill/issues/267)
result.add(new DisabledDuration(startDate, endDate));
}
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 3e7cd5b..c971588 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
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -17,7 +19,6 @@
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.List;
@@ -26,15 +27,10 @@ import java.util.SortedSet;
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;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
@@ -47,37 +43,14 @@ public class DefaultBillingEventSet extends TreeSet<BillingEvent> implements Sor
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) {
+ public DefaultBillingEventSet(final boolean accountAutoInvoiceOff, final BillingMode recurringBillingMode) {
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()
- */
- @Override
public boolean isAccountAutoInvoiceOff() {
return accountAutoInvoiceOff;
}
@@ -87,23 +60,12 @@ public class DefaultBillingEventSet extends TreeSet<BillingEvent> implements Sor
return recurringBillingMode;
}
- /* (non-Javadoc)
- * @see org.killbill.billing.junction.plumbing.billing.BillingEventSet#getSubscriptionIdsWithAutoInvoiceOff()
- */
@Override
public List<UUID> getSubscriptionIdsWithAutoInvoiceOff() {
return subscriptionIdsWithAutoInvoiceOff;
}
@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
@@ -121,7 +83,6 @@ public class DefaultBillingEventSet extends TreeSet<BillingEvent> implements Sor
return result;
}
-
@Override
public String toString() {
return "DefaultBillingEventSet [accountAutoInvoiceOff=" + accountAutoInvoiceOff
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 71bf65d..ede4a6d 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
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -17,7 +19,6 @@
package org.killbill.billing.junction.plumbing.billing;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
@@ -25,8 +26,6 @@ import java.util.UUID;
import javax.annotation.Nullable;
-import org.joda.time.DateTimeZone;
-import org.joda.time.LocalDate;
import org.killbill.billing.ObjectType;
import org.killbill.billing.account.api.AccountApiException;
import org.killbill.billing.account.api.AccountInternalApi;
@@ -35,6 +34,7 @@ import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.catalog.api.CatalogApiException;
import org.killbill.billing.catalog.api.CatalogService;
import org.killbill.billing.catalog.api.StaticCatalog;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
import org.killbill.billing.entitlement.api.SubscriptionEventType;
import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
import org.killbill.billing.invoice.api.DryRunArguments;
@@ -92,7 +92,7 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
final StaticCatalog currentCatalog = catalogService.getCurrentCatalog(context);
final ImmutableAccountData account = accountApi.getImmutableAccountDataById(accountId, context);
- final DefaultBillingEventSet result = new DefaultBillingEventSet(false, currentCatalog.getRecurringBillingMode(), account.getTimeZone());
+ final DefaultBillingEventSet result = new DefaultBillingEventSet(false, currentCatalog.getRecurringBillingMode());
@@ -102,7 +102,7 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
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) {
- return new DefaultBillingEventSet(true, currentCatalog.getRecurringBillingMode(), account.getTimeZone()); // billing is off, we are done
+ return new DefaultBillingEventSet(true, currentCatalog.getRecurringBillingMode()); // billing is off, we are done
}
addBillingEventsForBundles(bundles, account, dryRunArguments, context, result, skippedSubscriptions);
@@ -181,14 +181,13 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
for (final SubscriptionBase subscription : subscriptions) {
// The subscription did not even start, so there is nothing to do yet, we can skip and avoid some NPE down the line when calculating the BCD
- if (subscription.getState() == null) {
+ if (subscription.getState() == EntitlementState.PENDING) {
continue;
}
final List<EffectiveSubscriptionInternalEvent> billingTransitions = subscriptionApi.getBillingTransitions(subscription, context);
if (billingTransitions.isEmpty() ||
(billingTransitions.get(0).getTransitionType() != SubscriptionBaseTransitionType.CREATE &&
- billingTransitions.get(0).getTransitionType() != SubscriptionBaseTransitionType.MIGRATE_BILLING &&
billingTransitions.get(0).getTransitionType() != SubscriptionBaseTransitionType.TRANSFER)) {
log.warn("Skipping billing events for subscription " + subscription.getId() + ": Does not start with a valid CREATE transition");
skipSubscriptionsSet.add(subscription.getId());
diff --git a/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java b/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
index 4327199..568a786 100644
--- a/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
+++ b/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
@@ -23,6 +23,9 @@ import java.util.UUID;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.killbill.billing.GuicyKillbillTestSuiteWithEmbeddedDB;
+import org.killbill.billing.ObjectType;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountApiException;
import org.killbill.billing.account.api.AccountData;
import org.killbill.billing.account.api.AccountUserApi;
import org.killbill.billing.api.TestApiListener;
@@ -40,7 +43,9 @@ import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
import org.killbill.billing.subscription.api.SubscriptionBaseService;
import org.killbill.billing.subscription.engine.core.DefaultSubscriptionBaseService;
+import org.killbill.billing.util.cache.Cachable.CacheType;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.bus.api.PersistentBus;
import org.killbill.clock.ClockMock;
import org.slf4j.Logger;
@@ -83,6 +88,8 @@ public abstract class JunctionTestSuiteWithEmbeddedDB extends GuicyKillbillTestS
@Inject
protected EntitlementService entitlementService;
@Inject
+ protected NonEntityDao nonEntityDao;
+ @Inject
protected InternalCallContextFactory internalCallContextFactory;
protected Catalog catalog;
@@ -206,6 +213,14 @@ public abstract class JunctionTestSuiteWithEmbeddedDB extends GuicyKillbillTestS
.build();
}
+ protected Account createAccount(final AccountData accountData) throws AccountApiException {
+ final Account account = accountApi.createAccount(accountData, callContext);
+
+ refreshCallContext(account.getId());
+
+ return account;
+ }
+
protected void assertListenerStatus() {
testListener.assertListenerStatus();
}
diff --git a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultBillingEvent.java b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultBillingEvent.java
index aa47d70..fbd53bf 100644
--- a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultBillingEvent.java
+++ b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultBillingEvent.java
@@ -136,7 +136,7 @@ public class TestDefaultBillingEvent extends JunctionTestSuiteNoDB {
public void testEventTotalOrdering() {
final BillingEvent event0 = createEvent(subscription(ID_ZERO), new DateTime("2012-01-01T00:02:04.000Z"), SubscriptionBaseTransitionType.CREATE, 1L);
final BillingEvent event1 = createEvent(subscription(ID_ZERO), new DateTime("2012-01-01T00:02:04.000Z"), SubscriptionBaseTransitionType.CANCEL, 2L);
- final BillingEvent event2 = createEvent(subscription(ID_ZERO), new DateTime("2012-01-01T00:02:04.000Z"), SubscriptionBaseTransitionType.RE_CREATE, 3L);
+ final BillingEvent event2 = createEvent(subscription(ID_ZERO), new DateTime("2012-01-01T00:02:04.000Z"), SubscriptionBaseTransitionType.CANCEL, 3L);
final SortedSet<BillingEvent> set = new TreeSet<BillingEvent>();
set.add(event2);
diff --git a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
index d413692..c5cc8a9 100644
--- a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
+++ b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
@@ -54,12 +54,11 @@ public class TestDefaultInternalBillingApi extends JunctionTestSuiteWithEmbedded
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
- final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+ final Account account = createAccount(getAccountData(7));
- testListener.pushExpectedEvent(NextEvent.CREATE);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
final SubscriptionBase subscription = subscriptionInternalApi.getSubscriptionFromId(entitlement.getId(), internalCallContext);
assertListenerStatus();
@@ -191,12 +190,11 @@ public class TestDefaultInternalBillingApi extends JunctionTestSuiteWithEmbedded
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
- final Account account = accountApi.createAccount(getAccountData(7), callContext);
- final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+ final Account account = createAccount(getAccountData(7));
- testListener.pushExpectedEvent(NextEvent.CREATE);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
final SubscriptionBase subscription = subscriptionInternalApi.getSubscriptionFromId(entitlement.getId(), internalCallContext);
assertListenerStatus();
NEWS 3(+3 -0)
diff --git a/NEWS b/NEWS
index 8001aa9..3201911 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,6 @@
+0.16.2
+ See https://github.com/killbill/killbill/releases/tag/killbill-0.16.2
+
0.16.1
See https://github.com/killbill/killbill/releases/tag/killbill-0.16.1
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/api/DefaultOverdueApi.java b/overdue/src/main/java/org/killbill/billing/overdue/api/DefaultOverdueApi.java
index a0294f3..8540b0e 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/api/DefaultOverdueApi.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/api/DefaultOverdueApi.java
@@ -65,7 +65,7 @@ public class DefaultOverdueApi implements OverdueApi {
}
private InternalTenantContext createInternalTenantContext(final TenantContext tenantContext) {
- // Only tenantRecordId will be populated-- this is important to always create the (ehcache) key the same way
- return internalCallContextFactory.createInternalTenantContext(tenantContext);
+ // Only tenantRecordId will be populated -- this is important to always create the (ehcache) key the same way
+ return internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(tenantContext);
}
}
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/api/DefaultOverdueInternalApi.java b/overdue/src/main/java/org/killbill/billing/overdue/api/DefaultOverdueInternalApi.java
index a2701e4..3e6aad6 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/api/DefaultOverdueInternalApi.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/api/DefaultOverdueInternalApi.java
@@ -84,8 +84,8 @@ public class DefaultOverdueInternalApi implements OverdueInternalApi {
@Override
public OverdueState getOverdueStateFor(final ImmutableAccountData overdueable, final TenantContext context) throws OverdueException {
try {
- final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(context);
- final BlockingState blockingStateForService = accessApi.getBlockingStateForService(overdueable.getId(), BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, internalCallContextFactory.createInternalTenantContext(context));
+ final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(overdueable.getId(), context);
+ final BlockingState blockingStateForService = accessApi.getBlockingStateForService(overdueable.getId(), BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, internalTenantContext);
final String stateName = blockingStateForService != null ? blockingStateForService.getStateName() : OverdueWrapper.CLEAR_STATE_NAME;
final OverdueConfig overdueConfig = overdueConfigCache.getOverdueConfig(internalTenantContext);
final OverdueStateSet states = ((DefaultOverdueConfig) overdueConfig).getOverdueStatesAccount();
@@ -99,7 +99,7 @@ public class DefaultOverdueInternalApi implements OverdueInternalApi {
public BillingState getBillingStateFor(final ImmutableAccountData overdueable, final TenantContext context) throws OverdueException {
log.debug("Billing state of of {} requested", overdueable.getId());
- final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(context);
+ final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(overdueable.getId(), context);
final OverdueWrapper wrapper = factory.createOverdueWrapperFor(overdueable, internalTenantContext);
return wrapper.billingState(internalTenantContext);
}
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java b/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
index 2c942f6..91a0e03 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
@@ -318,7 +318,7 @@ public class OverdueStateApplicator {
computeEntitlementsToCancel(account, toBeCancelled, callContext);
try {
- entitlementInternalApi.cancel(toBeCancelled, new LocalDate(clock.getUTCNow(), account.getTimeZone()), actionPolicy, ImmutableList.<PluginProperty>of(), context);
+ entitlementInternalApi.cancel(toBeCancelled, clock.getToday(account.getTimeZone()), actionPolicy, ImmutableList.<PluginProperty>of(), context);
} catch (final EntitlementApiException e) {
throw new OverdueException(e);
}
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultDuration.java b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultDuration.java
index 5528e14..d1d73c2 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultDuration.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultDuration.java
@@ -23,6 +23,7 @@ import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
import org.joda.time.Period;
import org.killbill.billing.catalog.api.Duration;
@@ -62,13 +63,30 @@ public class DefaultDuration extends ValidatingConfig<DefaultOverdueConfig> impl
case YEARS:
return dateTime.plusYears(number);
case UNLIMITED:
- return dateTime.plusYears(100);
default:
- return dateTime;
+ throw new IllegalStateException("Unexpected duration unit " + unit);
}
}
@Override
+ public LocalDate addToLocalDate(final LocalDate localDate) {
+ if ((number == null) && (unit != TimeUnit.UNLIMITED)) {
+ return localDate;
+ }
+
+ switch (unit) {
+ case DAYS:
+ return localDate.plusDays(number);
+ case MONTHS:
+ return localDate.plusMonths(number);
+ case YEARS:
+ return localDate.plusYears(number);
+ case UNLIMITED:
+ default:
+ throw new IllegalStateException("Unexpected duration unit " + unit);
+ }
+ }
+ @Override
public Period toJodaPeriod() {
if ((number == null) && (unit != TimeUnit.UNLIMITED)) {
return new Period();
@@ -82,9 +100,8 @@ public class DefaultDuration extends ValidatingConfig<DefaultOverdueConfig> impl
case YEARS:
return new Period().withYears(number);
case UNLIMITED:
- return new Period().withYears(100);
default:
- return new Period();
+ throw new IllegalStateException("Unexpected duration unit " + unit);
}
}
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/notification/DefaultOverduePosterBase.java b/overdue/src/main/java/org/killbill/billing/overdue/notification/DefaultOverduePosterBase.java
index b737976..463d08c 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/notification/DefaultOverduePosterBase.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/notification/DefaultOverduePosterBase.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -25,6 +25,7 @@ import org.joda.time.DateTime;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.overdue.service.DefaultOverdueService;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
@@ -48,10 +49,10 @@ public abstract class DefaultOverduePosterBase implements OverduePoster {
private final EntitySqlDaoTransactionalJdbiWrapper transactionalSqlDao;
public DefaultOverduePosterBase(final NotificationQueueService notificationQueueService,
- final IDBI dbi, final Clock clock,
- final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao) {
+ final IDBI dbi, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher,
+ final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory) {
this.notificationQueueService = notificationQueueService;
- this.transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao);
+ this.transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory);
}
@Override
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueAsyncBusPoster.java b/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueAsyncBusPoster.java
index 967d5b6..d6b7a44 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueAsyncBusPoster.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueAsyncBusPoster.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -22,6 +22,7 @@ import java.util.Collection;
import org.joda.time.DateTime;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
import org.killbill.clock.Clock;
@@ -36,9 +37,9 @@ public class OverdueAsyncBusPoster extends DefaultOverduePosterBase {
@Inject
public OverdueAsyncBusPoster(final NotificationQueueService notificationQueueService,
- final IDBI dbi, final Clock clock,
- final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao) {
- super(notificationQueueService, dbi, clock, cacheControllerDispatcher, nonEntityDao);
+ final IDBI dbi, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher,
+ final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory) {
+ super(notificationQueueService, dbi, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory);
}
@Override
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueCheckPoster.java b/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueCheckPoster.java
index 0c118c4..1119e53 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueCheckPoster.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueCheckPoster.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -22,6 +22,7 @@ import java.util.Collection;
import org.joda.time.DateTime;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
import org.killbill.clock.Clock;
@@ -36,9 +37,9 @@ public class OverdueCheckPoster extends DefaultOverduePosterBase {
@Inject
public OverdueCheckPoster(final NotificationQueueService notificationQueueService,
- final IDBI dbi, final Clock clock,
- final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao) {
- super(notificationQueueService, dbi, clock, cacheControllerDispatcher, nonEntityDao);
+ final IDBI dbi, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher,
+ final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory) {
+ super(notificationQueueService, dbi, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory);
}
@Override
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/notification/TestDefaultOverdueCheckPoster.java b/overdue/src/test/java/org/killbill/billing/overdue/notification/TestDefaultOverdueCheckPoster.java
index 8d2a884..03e91fa 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/notification/TestDefaultOverdueCheckPoster.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/notification/TestDefaultOverdueCheckPoster.java
@@ -48,7 +48,7 @@ public class TestDefaultOverdueCheckPoster extends OverdueTestSuiteWithEmbeddedD
@BeforeMethod(groups = "slow")
public void beforeMethod() throws Exception {
super.beforeMethod();
- entitySqlDaoTransactionalJdbiWrapper = new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao);
+ entitySqlDaoTransactionalJdbiWrapper = new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory);
overdueQueue = notificationQueueService.getNotificationQueue(DefaultOverdueService.OVERDUE_SERVICE_NAME,
OverdueCheckNotifier.OVERDUE_CHECK_NOTIFIER_QUEUE);
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPayment.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPayment.java
index f6c86e6..14e7ab0 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPayment.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPayment.java
@@ -72,21 +72,26 @@ public class DefaultPayment extends EntityBase implements Payment {
private static BigDecimal getAmountForType(final Iterable<PaymentTransaction> transactions, final TransactionType transactiontype) {
BigDecimal result = BigDecimal.ZERO;
- final Iterable<PaymentTransaction> filtered = Iterables.filter(transactions, new Predicate<PaymentTransaction>() {
- @Override
- public boolean apply(final PaymentTransaction input) {
- return input.getTransactionType() == transactiontype && TransactionStatus.SUCCESS.equals(input.getTransactionStatus());
+ BigDecimal processedResult = BigDecimal.ZERO;
+ boolean shouldUseProcessedAmount = true;
+
+ for (final PaymentTransaction transaction : transactions) {
+ if (transaction.getTransactionType() != transactiontype || !TransactionStatus.SUCCESS.equals(transaction.getTransactionStatus())) {
+ continue;
}
- });
- if (TransactionType.AUTHORIZE.equals(transactiontype) && filtered.iterator().hasNext()) {
- // HACK - For multi-step AUTH, don't sum the individual transactions
- result = filtered.iterator().next().getAmount();
- } else {
- for (final PaymentTransaction dpt : filtered) {
- result = result.add(dpt.getAmount());
+
+ result = result.add(transaction.getAmount());
+
+ shouldUseProcessedAmount = shouldUseProcessedAmount && transaction.getCurrency().equals(transaction.getProcessedCurrency()) && transaction.getProcessedAmount() != null;
+ processedResult = shouldUseProcessedAmount ? processedResult.add(transaction.getProcessedAmount()) : BigDecimal.ZERO;
+
+ // For multi-step AUTH, don't sum the individual transactions
+ if (TransactionType.AUTHORIZE.equals(transactiontype)) {
+ break;
}
}
- return result;
+
+ return shouldUseProcessedAmount ? processedResult : result;
}
@Override
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
index 3da498d..406e8d8 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
@@ -26,13 +26,13 @@ import javax.annotation.Nullable;
import javax.inject.Inject;
import org.killbill.billing.ErrorCode;
+import org.killbill.billing.ObjectType;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.payment.core.PaymentMethodProcessor;
import org.killbill.billing.payment.core.PaymentProcessor;
import org.killbill.billing.payment.core.PluginControlPaymentProcessor;
-import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
import org.killbill.billing.util.UUIDs;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
@@ -42,8 +42,6 @@ import org.killbill.billing.util.entity.Pagination;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.collect.ImmutableList;
-
public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
private static final boolean SHOULD_LOCK_ACCOUNT = true;
@@ -382,17 +380,17 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
@Override
public Pagination<Payment> getPayments(final Long offset, final Long limit, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) {
- return paymentProcessor.getPayments(offset, limit, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+ return paymentProcessor.getPayments(offset, limit, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
}
@Override
public Pagination<Payment> getPayments(final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext) throws PaymentApiException {
- return paymentProcessor.getPayments(offset, limit, pluginName, withPluginInfo, properties, tenantContext, internalCallContextFactory.createInternalTenantContext(tenantContext));
+ return paymentProcessor.getPayments(offset, limit, pluginName, withPluginInfo, properties, tenantContext, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(tenantContext));
}
@Override
public Payment getPayment(final UUID paymentId, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentApiException {
- final Payment payment = paymentProcessor.getPayment(paymentId, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+ final Payment payment = paymentProcessor.getPayment(paymentId, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(paymentId, ObjectType.PAYMENT, context));
if (payment == null) {
throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT, paymentId);
}
@@ -402,7 +400,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
@Override
public Payment getPaymentByExternalKey(final String paymentExternalKey, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext)
throws PaymentApiException {
- final Payment payment = paymentProcessor.getPaymentByExternalKey(paymentExternalKey, withPluginInfo, properties, tenantContext, internalCallContextFactory.createInternalTenantContext(tenantContext));
+ final Payment payment = paymentProcessor.getPaymentByExternalKey(paymentExternalKey, withPluginInfo, properties, tenantContext, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(tenantContext));
if (payment == null) {
throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT, paymentExternalKey);
}
@@ -411,12 +409,12 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
@Override
public Pagination<Payment> searchPayments(final String searchKey, final Long offset, final Long limit, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) {
- return paymentProcessor.searchPayments(searchKey, offset, limit, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+ return paymentProcessor.searchPayments(searchKey, offset, limit, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
}
@Override
public Pagination<Payment> searchPayments(final String searchKey, final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentApiException {
- return paymentProcessor.searchPayments(searchKey, offset, limit, pluginName, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+ return paymentProcessor.searchPayments(searchKey, offset, limit, pluginName, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
}
@Override
@@ -431,39 +429,39 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
@Override
public List<PaymentMethod> getAccountPaymentMethods(final UUID accountId, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context)
throws PaymentApiException {
- return paymentMethodProcessor.getPaymentMethods(accountId, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+ return paymentMethodProcessor.getPaymentMethods(accountId, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(accountId, context));
}
@Override
public PaymentMethod getPaymentMethodById(final UUID paymentMethodId, final boolean includedDeleted, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context)
throws PaymentApiException {
- return paymentMethodProcessor.getPaymentMethodById(paymentMethodId, includedDeleted, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+ return paymentMethodProcessor.getPaymentMethodById(paymentMethodId, includedDeleted, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(paymentMethodId, ObjectType.PAYMENT_METHOD, context));
}
@Override
public PaymentMethod getPaymentMethodByExternalKey(final String paymentMethodExternalKey, final boolean includedInactive, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context)
throws PaymentApiException {
- return paymentMethodProcessor.getPaymentMethodByExternalKey(paymentMethodExternalKey, includedInactive, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+ return paymentMethodProcessor.getPaymentMethodByExternalKey(paymentMethodExternalKey, includedInactive, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
}
@Override
public Pagination<PaymentMethod> getPaymentMethods(final Long offset, final Long limit, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) {
- return paymentMethodProcessor.getPaymentMethods(offset, limit, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+ return paymentMethodProcessor.getPaymentMethods(offset, limit, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
}
@Override
public Pagination<PaymentMethod> getPaymentMethods(final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentApiException {
- return paymentMethodProcessor.getPaymentMethods(offset, limit, pluginName, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+ return paymentMethodProcessor.getPaymentMethods(offset, limit, pluginName, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
}
@Override
public Pagination<PaymentMethod> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) {
- return paymentMethodProcessor.searchPaymentMethods(searchKey, offset, limit, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+ return paymentMethodProcessor.searchPaymentMethods(searchKey, offset, limit, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
}
@Override
public Pagination<PaymentMethod> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentApiException {
- return paymentMethodProcessor.searchPaymentMethods(searchKey, offset, limit, pluginName, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+ return paymentMethodProcessor.searchPaymentMethods(searchKey, offset, limit, pluginName, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
}
@Override
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
index 15dc191..c71407d 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
@@ -211,11 +211,23 @@ public class IncompletePaymentTransactionTask extends CompletionTaskBase<Payment
// Recompute new lastSuccessPaymentState. This is important to be able to allow new operations on the state machine (for e.g an AUTH_SUCCESS would now allow a CAPTURE operation)
final String lastSuccessPaymentState = paymentStateMachineHelper.isSuccessState(newPaymentState) ? newPaymentState : null;
- // Update the processedAmount, processedCurrency if we got a paymentTransactionInfoPlugin from the plugin and if this is a non error state
- final BigDecimal processedAmount = (paymentTransactionInfoPlugin != null && isPendingOrFinalTransactionStatus(transactionStatus)) ?
- paymentTransactionInfoPlugin.getAmount() : paymentTransaction.getProcessedAmount();
- final Currency processedCurrency = (paymentTransactionInfoPlugin != null && isPendingOrFinalTransactionStatus(transactionStatus)) ?
- paymentTransactionInfoPlugin.getCurrency() : paymentTransaction.getProcessedCurrency();
+ // Update processedAmount and processedCurrency
+ final BigDecimal processedAmount;
+ if (TransactionStatus.SUCCESS.equals(transactionStatus) || TransactionStatus.PENDING.equals(transactionStatus)) {
+ if (paymentTransactionInfoPlugin == null || paymentTransactionInfoPlugin.getAmount() == null) {
+ processedAmount = paymentTransaction.getProcessedAmount();
+ } else {
+ processedAmount = paymentTransactionInfoPlugin.getAmount();
+ }
+ } else {
+ processedAmount = BigDecimal.ZERO;
+ }
+ final Currency processedCurrency;
+ if (paymentTransactionInfoPlugin == null || paymentTransactionInfoPlugin.getCurrency() == null) {
+ processedCurrency = paymentTransaction.getProcessedCurrency();
+ } else {
+ processedCurrency = paymentTransactionInfoPlugin.getCurrency();
+ }
// Update the gatewayErrorCode, gatewayError if we got a paymentTransactionInfoPlugin
final String gatewayErrorCode = paymentTransactionInfoPlugin != null ? paymentTransactionInfoPlugin.getGatewayErrorCode() : paymentTransaction.getGatewayErrorCode();
@@ -239,12 +251,6 @@ public class IncompletePaymentTransactionTask extends CompletionTaskBase<Payment
return (newTransactionStatus != TransactionStatus.UNKNOWN) ? newTransactionStatus : currentTransactionStatus;
}
- private boolean isPendingOrFinalTransactionStatus(final TransactionStatus transactionStatus) {
- return (transactionStatus == TransactionStatus.PENDING ||
- transactionStatus == TransactionStatus.SUCCESS ||
- transactionStatus == TransactionStatus.PAYMENT_FAILURE);
- }
-
private PaymentPluginApi getPaymentPluginApi(final PaymentModelDao item, final String pluginName) {
final PaymentPluginApi pluginApi = pluginRegistry.getServiceForName(pluginName);
Preconditions.checkState(pluginApi != null, "Janitor IncompletePaymentTransactionTask cannot retrieve PaymentPluginApi for plugin %s (payment id %s), skipping", pluginName, item.getId());
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlInitiated.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlInitiated.java
index 4ead401..9a87984 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlInitiated.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlInitiated.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -63,6 +63,7 @@ public class DefaultControlInitiated implements LeavingStateCallback {
final PaymentModelDao payment = paymentDao.getPayment(stateContext.getPaymentId(), stateContext.getInternalCallContext());
Preconditions.checkNotNull(payment, "payment cannot be null for id " + stateContext.getPaymentId());
stateContext.setPaymentExternalKey(payment.getExternalKey());
+ stateContext.setPaymentMethodId(payment.getPaymentMethodId());
} else if (stateContext.getPaymentExternalKey() == null) {
stateContext.setPaymentExternalKey(UUIDs.randomUUID().toString());
}
@@ -74,6 +75,11 @@ public class DefaultControlInitiated implements LeavingStateCallback {
stateContext.setPaymentTransactionExternalKey(UUIDs.randomUUID().toString());
}
+ if (stateContext.getPaymentMethodId() == null) {
+ // Similar logic in PaymentAutomatonRunner
+ stateContext.setPaymentMethodId(stateContext.getAccount().getPaymentMethodId());
+ }
+
if (state.getName().equals(initialState.getName()) || state.getName().equals(retriedState.getName())) {
try {
//
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentStateControlContext.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentStateControlContext.java
index 8b51735..2841907 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentStateControlContext.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentStateControlContext.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -72,7 +72,6 @@ public class PaymentStateControlContext extends PaymentStateContext {
this.result = result;
}
-
public PaymentTransaction getCurrentTransaction() {
if (result == null || result.getTransactions() == null) {
return null;
@@ -80,7 +79,8 @@ public class PaymentStateControlContext extends PaymentStateContext {
return Iterables.tryFind(result.getTransactions(), new Predicate<PaymentTransaction>() {
@Override
public boolean apply(final PaymentTransaction input) {
- return ((DefaultPaymentTransaction) input).getAttemptId().equals(getAttemptId());
+ final DefaultPaymentTransaction defaultPaymentTransaction = (DefaultPaymentTransaction) input;
+ return defaultPaymentTransaction.getAttemptId() == null ? getAttemptId() == null : defaultPaymentTransaction.getAttemptId().equals(getAttemptId());
}
}).orNull();
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java
index 2b3a215..c6545dc 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java
@@ -30,6 +30,7 @@ import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.api.TransactionStatus;
+import org.killbill.billing.payment.api.TransactionType;
import org.killbill.billing.payment.dao.PaymentDao;
import org.killbill.billing.payment.dao.PaymentMethodModelDao;
import org.killbill.billing.payment.dao.PaymentModelDao;
@@ -89,7 +90,10 @@ public class PaymentAutomatonDAOHelper {
if (existingTransactions.isEmpty()) {
throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_SUCCESS_PAYMENT, paymentStateContext.getPaymentId());
}
- if (paymentStateContext.getCurrency() != null && existingTransactions.get(0).getCurrency() != paymentStateContext.getCurrency()) {
+ if (paymentStateContext.getCurrency() != null &&
+ existingTransactions.get(0).getCurrency() != paymentStateContext.getCurrency() &&
+ !TransactionType.CHARGEBACK.equals(paymentStateContext.getTransactionType())) {
+ // Note that we allow chargebacks in a different currency
throw new PaymentApiException(ErrorCode.PAYMENT_INVALID_PARAMETER, "currency", " should be " + existingTransactions.get(0).getCurrency() + " to match other existing transactions");
}
@@ -103,8 +107,22 @@ public class PaymentAutomatonDAOHelper {
public void processPaymentInfoPlugin(final TransactionStatus transactionStatus, @Nullable final PaymentTransactionInfoPlugin paymentInfoPlugin,
final String currentPaymentStateName) {
- final BigDecimal processedAmount = paymentInfoPlugin == null ? null : paymentInfoPlugin.getAmount();
- final Currency processedCurrency = paymentInfoPlugin == null ? null : paymentInfoPlugin.getCurrency();
+ final BigDecimal processedAmount;
+ if (TransactionStatus.SUCCESS.equals(transactionStatus) || TransactionStatus.PENDING.equals(transactionStatus)) {
+ if (paymentInfoPlugin == null || paymentInfoPlugin.getAmount() == null) {
+ processedAmount = paymentStateContext.getAmount();
+ } else {
+ processedAmount = paymentInfoPlugin.getAmount();
+ }
+ } else {
+ processedAmount = BigDecimal.ZERO;
+ }
+ final Currency processedCurrency;
+ if (paymentInfoPlugin == null || paymentInfoPlugin.getCurrency() == null) {
+ processedCurrency = paymentStateContext.getCurrency();
+ } else {
+ processedCurrency = paymentInfoPlugin.getCurrency();
+ }
final String gatewayErrorCode = paymentInfoPlugin == null ? null : paymentInfoPlugin.getGatewayErrorCode();
final String gatewayErrorMsg = paymentInfoPlugin == null ? null : paymentInfoPlugin.getGatewayError();
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java
index a03d1eb..2c849ca 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -43,6 +43,7 @@ import org.killbill.billing.payment.api.PaymentTransaction;
import org.killbill.billing.payment.api.TransactionStatus;
import org.killbill.billing.payment.api.TransactionType;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.entity.Entity;
import org.killbill.billing.util.entity.Pagination;
@@ -67,7 +68,7 @@ import com.google.common.collect.Iterables;
public class DefaultPaymentDao implements PaymentDao {
- private final static Logger log = LoggerFactory.getLogger(DefaultPaymentDao.class);
+ private static final Logger log = LoggerFactory.getLogger(DefaultPaymentDao.class);
private final EntitySqlDaoTransactionalJdbiWrapper transactionalSqlDao;
private final DefaultPaginationSqlDaoHelper paginationHelper;
@@ -75,8 +76,9 @@ public class DefaultPaymentDao implements PaymentDao {
private final Clock clock;
@Inject
- public DefaultPaymentDao(final IDBI dbi, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao, final PersistentBus eventBus) {
- this.transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao);
+ public DefaultPaymentDao(final IDBI dbi, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher,
+ final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory, final PersistentBus eventBus) {
+ this.transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory);
this.paginationHelper = new DefaultPaginationSqlDaoHelper(transactionalSqlDao);
this.eventBus = eventBus;
this.clock = clock;
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentModelDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentModelDao.java
index 612fed7..b15be00 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentModelDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentModelDao.java
@@ -1,7 +1,8 @@
/*
- * Copyright 2014 Groupon, Inc
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Groupon licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -103,6 +104,21 @@ public class PaymentModelDao extends EntityModelDaoBase implements EntityModelDa
}
@Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("PaymentModelDao{");
+ sb.append("accountId=").append(accountId);
+ sb.append(", paymentNumber=").append(paymentNumber);
+ sb.append(", paymentMethodId=").append(paymentMethodId);
+ sb.append(", externalKey='").append(externalKey).append('\'');
+ sb.append(", stateName='").append(stateName).append('\'');
+ sb.append(", lastSuccessStateName='").append(lastSuccessStateName).append('\'');
+ sb.append(", createdDate=").append(createdDate);
+ sb.append(", updatedDate=").append(updatedDate);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
public boolean equals(final Object o) {
if (this == o) {
return true;
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentTransactionModelDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentTransactionModelDao.java
index ac0f0da..3f11cf9 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentTransactionModelDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentTransactionModelDao.java
@@ -1,7 +1,8 @@
/*
- * Copyright 2014 Groupon, Inc
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Groupon licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -225,6 +226,27 @@ public class PaymentTransactionModelDao extends EntityModelDaoBase implements En
}
@Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("PaymentTransactionModelDao{");
+ sb.append("attemptId=").append(attemptId);
+ sb.append(", paymentId=").append(paymentId);
+ sb.append(", transactionExternalKey='").append(transactionExternalKey).append('\'');
+ sb.append(", transactionType=").append(transactionType);
+ sb.append(", effectiveDate=").append(effectiveDate);
+ sb.append(", transactionStatus=").append(transactionStatus);
+ sb.append(", amount=").append(amount);
+ sb.append(", currency=").append(currency);
+ sb.append(", processedAmount=").append(processedAmount);
+ sb.append(", processedCurrency=").append(processedCurrency);
+ sb.append(", gatewayErrorCode='").append(gatewayErrorCode).append('\'');
+ sb.append(", gatewayErrorMsg='").append(gatewayErrorMsg).append('\'');
+ sb.append(", createdDate=").append(createdDate);
+ sb.append(", updatedDate=").append(updatedDate);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (paymentId != null ? paymentId.hashCode() : 0);
diff --git a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
index a86eb99..e0ea154 100644
--- a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
@@ -36,6 +36,7 @@ import org.killbill.billing.account.api.AccountInternalApi;
import org.killbill.billing.account.api.ImmutableAccountData;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.control.plugin.api.OnFailurePaymentControlResult;
import org.killbill.billing.control.plugin.api.OnSuccessPaymentControlResult;
import org.killbill.billing.control.plugin.api.PaymentApiType;
@@ -159,11 +160,19 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
final UUID invoiceId = getInvoiceId(pluginProperties);
existingInvoicePayment = invoiceApi.getInvoicePaymentForAttempt(paymentControlContext.getPaymentId(), internalContext);
if (existingInvoicePayment != null && existingInvoicePayment.isSuccess()) {
+ // Only one successful purchase per payment (the invoice could be linked to multiple successful payments though)
log.info("onSuccessCall was already completed for payment purchase: " + paymentControlContext.getPaymentId());
} else {
- log.debug("Notifying invoice of successful payment: id={}, amount={}, currency={}, invoiceId={}", paymentControlContext.getPaymentId(), paymentControlContext.getAmount(), paymentControlContext.getCurrency(), invoiceId);
+ final BigDecimal invoicePaymentAmount;
+ if (paymentControlContext.getCurrency() == paymentControlContext.getProcessedCurrency()) {
+ invoicePaymentAmount = paymentControlContext.getProcessedAmount();
+ } else {
+ log.warn("Currency {} of invoice payment {} doesn't match invoice currency {}, assuming it is a full payment" , paymentControlContext.getProcessedCurrency(), paymentControlContext.getPaymentId(), paymentControlContext.getCurrency());
+ invoicePaymentAmount = paymentControlContext.getAmount();
+ }
+ log.debug("Notifying invoice of successful payment: id={}, amount={}, currency={}, invoiceId={}", paymentControlContext.getPaymentId(), invoicePaymentAmount, paymentControlContext.getCurrency(), invoiceId);
invoiceApi.notifyOfPayment(invoiceId,
- paymentControlContext.getAmount(),
+ invoicePaymentAmount,
paymentControlContext.getCurrency(),
paymentControlContext.getProcessedCurrency(),
paymentControlContext.getPaymentId(),
@@ -174,23 +183,34 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
break;
case REFUND:
- existingInvoicePayment = invoiceApi.getInvoicePaymentForRefund(paymentControlContext.getPaymentId(), internalContext);
- if (existingInvoicePayment != null) {
- log.info("onSuccessCall was already completed for payment refund: " + paymentControlContext.getPaymentId());
- } else {
- final Map<UUID, BigDecimal> idWithAmount = extractIdsWithAmountFromProperties(pluginProperties);
- final PluginProperty prop = getPluginProperty(pluginProperties, PROP_IPCD_REFUND_WITH_ADJUSTMENTS);
- final boolean isAdjusted = prop != null ? Boolean.valueOf((String) prop.getValue()) : false;
- invoiceApi.createRefund(paymentControlContext.getPaymentId(), paymentControlContext.getAmount(), isAdjusted, idWithAmount, paymentControlContext.getTransactionExternalKey(), internalContext);
- }
+ final Map<UUID, BigDecimal> idWithAmount = extractIdsWithAmountFromProperties(pluginProperties);
+ final PluginProperty prop = getPluginProperty(pluginProperties, PROP_IPCD_REFUND_WITH_ADJUSTMENTS);
+ final boolean isAdjusted = prop != null ? Boolean.valueOf((String) prop.getValue()) : false;
+ invoiceApi.createRefund(paymentControlContext.getPaymentId(), paymentControlContext.getAmount(), isAdjusted, idWithAmount, paymentControlContext.getTransactionExternalKey(), internalContext);
break;
case CHARGEBACK:
existingInvoicePayment = invoiceApi.getInvoicePaymentForChargeback(paymentControlContext.getPaymentId(), internalContext);
if (existingInvoicePayment != null) {
+ // We don't support partial chargebacks (yet?)
log.info("onSuccessCall was already completed for payment chargeback: " + paymentControlContext.getPaymentId());
} else {
- invoiceApi.createChargeback(paymentControlContext.getPaymentId(), paymentControlContext.getProcessedAmount(), paymentControlContext.getProcessedCurrency(), internalContext);
+ final InvoicePayment linkedInvoicePayment = invoiceApi.getInvoicePaymentForAttempt(paymentControlContext.getPaymentId(), internalContext);
+
+ final BigDecimal amount;
+ final Currency currency;
+ if (linkedInvoicePayment.getCurrency().equals(paymentControlContext.getProcessedCurrency()) && paymentControlContext.getProcessedAmount() != null) {
+ amount = paymentControlContext.getProcessedAmount();
+ currency = paymentControlContext.getProcessedCurrency();
+ } else if (linkedInvoicePayment.getCurrency().equals(paymentControlContext.getCurrency()) && paymentControlContext.getAmount() != null) {
+ amount = paymentControlContext.getAmount();
+ currency = paymentControlContext.getCurrency();
+ } else {
+ amount = linkedInvoicePayment.getAmount();
+ currency = linkedInvoicePayment.getCurrency();
+ }
+
+ invoiceApi.createChargeback(paymentControlContext.getPaymentId(), amount, currency, internalContext);
}
break;
@@ -216,7 +236,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
try {
log.debug("Notifying invoice of failed payment: id={}, amount={}, currency={}, invoiceId={}", paymentControlContext.getPaymentId(), paymentControlContext.getAmount(), paymentControlContext.getCurrency(), invoiceId);
invoiceApi.notifyOfPayment(invoiceId,
- paymentControlContext.getAmount(),
+ BigDecimal.ZERO,
paymentControlContext.getCurrency(),
// processed currency may be null so we use currency; processed currency will be updated if/when payment succeeds
paymentControlContext.getCurrency(),
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
index d5c1aa5..d6e293e 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
@@ -182,7 +182,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
assertEquals(payment.getTransactions().get(0).getPaymentId(), payment.getId());
assertEquals(payment.getTransactions().get(0).getAmount().compareTo(requestedAmount), 0);
assertEquals(payment.getTransactions().get(0).getCurrency(), Currency.AED);
- assertEquals(payment.getTransactions().get(0).getProcessedAmount().compareTo(requestedAmount), 0);
+ assertEquals(payment.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.ZERO), 0);
assertEquals(payment.getTransactions().get(0).getProcessedCurrency(), Currency.AED);
assertEquals(payment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.PAYMENT_FAILURE);
@@ -405,7 +405,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
assertEquals(payment.getTransactions().get(0).getPaymentId(), payment.getId());
assertEquals(payment.getTransactions().get(0).getAmount().compareTo(requestedAmount), 0);
assertEquals(payment.getTransactions().get(0).getCurrency(), Currency.USD);
- assertEquals(payment.getTransactions().get(0).getProcessedAmount().compareTo(requestedAmount), 0); // This is weird...
+ assertEquals(payment.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.ZERO), 0);
assertEquals(payment.getTransactions().get(0).getProcessedCurrency(), Currency.USD);
assertEquals(payment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.PAYMENT_FAILURE);
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiNoDB.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiNoDB.java
index c19453b..83a2b65 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiNoDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiNoDB.java
@@ -74,7 +74,7 @@ public class TestPaymentApiNoDB extends PaymentTestSuiteNoDB {
public void beforeMethod() throws Exception {
super.beforeMethod();
final PaymentMethodPlugin paymentMethodInfo = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), true, null);
- testHelper.addTestPaymentMethod(account, paymentMethodInfo);
+ account = testHelper.addTestPaymentMethod(account, paymentMethodInfo);
}
@Test(groups = "fast")
@@ -169,18 +169,15 @@ public class TestPaymentApiNoDB extends PaymentTestSuiteNoDB {
final PaymentMethod initDefaultMethod = methods.get(0);
assertEquals(initDefaultMethod.getId(), account.getPaymentMethodId());
- final PaymentMethodPlugin newPaymenrMethod = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), true, null);
- final UUID newPaymentMethodId = paymentApi.addPaymentMethod(account, UUID.randomUUID().toString(), MockPaymentProviderPlugin.PLUGIN_NAME, true, newPaymenrMethod, PLUGIN_PROPERTIES, callContext);
- Mockito.when(account.getPaymentMethodId()).thenReturn(newPaymentMethodId);
+ final PaymentMethodPlugin newPaymentMethod = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), true, null);
+ account = testHelper.addTestPaymentMethod(account, newPaymentMethod, PLUGIN_PROPERTIES);
methods = paymentApi.getAccountPaymentMethods(account.getId(), false, PLUGIN_PROPERTIES, callContext);
assertEquals(methods.size(), 2);
- assertEquals(newPaymentMethodId, account.getPaymentMethodId());
-
boolean failed = false;
try {
- paymentApi.deletePaymentMethod(account, newPaymentMethodId, false, PLUGIN_PROPERTIES, callContext);
+ paymentApi.deletePaymentMethod(account, account.getPaymentMethodId(), false, PLUGIN_PROPERTIES, callContext);
} catch (final PaymentApiException e) {
failed = true;
}
@@ -191,7 +188,7 @@ public class TestPaymentApiNoDB extends PaymentTestSuiteNoDB {
assertEquals(methods.size(), 1);
// NOW retry with default payment method with special flag
- paymentApi.deletePaymentMethod(account, newPaymentMethodId, true, PLUGIN_PROPERTIES, callContext);
+ paymentApi.deletePaymentMethod(account, account.getPaymentMethodId(), true, PLUGIN_PROPERTIES, callContext);
methods = paymentApi.getAccountPaymentMethods(account.getId(), false, PLUGIN_PROPERTIES, callContext);
assertEquals(methods.size(), 0);
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiWithControl.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiWithControl.java
index aaffeda..acb1be0 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiWithControl.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiWithControl.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -23,17 +23,17 @@ import java.util.UUID;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.catalog.api.Currency;
-import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
-import org.killbill.billing.payment.PaymentTestSuiteWithEmbeddedDB;
-import org.killbill.billing.payment.provider.DefaultNoOpPaymentMethodPlugin;
-import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
import org.killbill.billing.control.plugin.api.OnFailurePaymentControlResult;
import org.killbill.billing.control.plugin.api.OnSuccessPaymentControlResult;
import org.killbill.billing.control.plugin.api.PaymentControlApiException;
import org.killbill.billing.control.plugin.api.PaymentControlContext;
import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
import org.killbill.billing.control.plugin.api.PriorPaymentControlResult;
+import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.PaymentTestSuiteWithEmbeddedDB;
+import org.killbill.billing.payment.provider.DefaultNoOpPaymentMethodPlugin;
+import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
import org.killbill.billing.payment.retry.DefaultFailureCallResult;
import org.killbill.billing.payment.retry.DefaultOnSuccessPaymentControlResult;
import org.testng.Assert;
@@ -45,45 +45,107 @@ import com.google.inject.Inject;
public class TestPaymentApiWithControl extends PaymentTestSuiteWithEmbeddedDB {
+ private static final PaymentOptions PAYMENT_OPTIONS = new PaymentOptions() {
+ @Override
+ public boolean isExternalPayment() {
+ return false;
+ }
+
+ @Override
+ public List<String> getPaymentControlPluginNames() {
+ return ImmutableList.of(TestPaymentControlPluginApi.PLUGIN_NAME);
+ }
+ };
+
@Inject
private OSGIServiceRegistration<PaymentControlPluginApi> controlPluginRegistry;
private Account account;
- private UUID newPaymentMethodId;
+ private TestPaymentControlPluginApi testPaymentControlPluginApi;
@BeforeMethod(groups = "slow")
public void beforeMethod() throws Exception {
super.beforeMethod();
account = testHelper.createTestAccount("bobo@gmail.com", true);
- final PaymentMethodPlugin paymentMethodInfo = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), false, null);
- newPaymentMethodId = paymentApi.addPaymentMethod(account, paymentMethodInfo.getExternalPaymentMethodId(), MockPaymentProviderPlugin.PLUGIN_NAME, false, paymentMethodInfo, ImmutableList.<PluginProperty>of(), callContext);
+ testPaymentControlPluginApi = new TestPaymentControlPluginApi();
controlPluginRegistry.registerService(new OSGIServiceDescriptor() {
- @Override
- public String getPluginSymbolicName() {
- return null;
- }
-
- @Override
- public String getPluginName() {
- return TestPaymentControlPluginApi.PLUGIN_NAME;
- }
-
- @Override
- public String getRegistrationName() {
- return TestPaymentControlPluginApi.PLUGIN_NAME;
- }
- }, new TestPaymentControlPluginApi(newPaymentMethodId));
+ @Override
+ public String getPluginSymbolicName() {
+ return null;
+ }
+
+ @Override
+ public String getPluginName() {
+ return TestPaymentControlPluginApi.PLUGIN_NAME;
+ }
+
+ @Override
+ public String getRegistrationName() {
+ return TestPaymentControlPluginApi.PLUGIN_NAME;
+ }
+ },
+ testPaymentControlPluginApi);
+ }
+ // Verify Payment control API can be used to change the paymentMethodId on the fly and this is reflected in the created Payment.
+ @Test(groups = "slow")
+ public void testCreateAuthWithControl() throws PaymentApiException {
+ final PaymentMethodPlugin paymentMethodInfo = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), false, null);
+ final UUID newPaymentMethodId = paymentApi.addPaymentMethod(account, paymentMethodInfo.getExternalPaymentMethodId(), MockPaymentProviderPlugin.PLUGIN_NAME, false, paymentMethodInfo, ImmutableList.<PluginProperty>of(), callContext);
+ testPaymentControlPluginApi.setNewPaymentMethodId(newPaymentMethodId);
+
+ final Payment payment = paymentApi.createAuthorizationWithPaymentControl(account, account.getPaymentMethodId(), null, BigDecimal.TEN, Currency.USD, UUID.randomUUID().toString(),
+ UUID.randomUUID().toString(), ImmutableList.<PluginProperty>of(), PAYMENT_OPTIONS, callContext);
+ Assert.assertEquals(payment.getPaymentMethodId(), newPaymentMethodId);
+ }
+
+ @Test(groups = "slow")
+ public void testCreateAuthWithControlCaptureNoControl() throws PaymentApiException {
+ final BigDecimal requestedAmount = BigDecimal.TEN;
+
+ Payment payment = paymentApi.createAuthorizationWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, UUID.randomUUID().toString(),
+ UUID.randomUUID().toString(), ImmutableList.<PluginProperty>of(), PAYMENT_OPTIONS, callContext);
+ Assert.assertEquals(payment.getAuthAmount().compareTo(requestedAmount), 0);
+ Assert.assertEquals(payment.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getTransactions().size(), 1);
+ Assert.assertNotNull(((DefaultPaymentTransaction) payment.getTransactions().get(0)).getAttemptId());
+
+ payment = paymentApi.createCapture(account, payment.getId(), payment.getAuthAmount(), payment.getCurrency(), UUID.randomUUID().toString(), ImmutableList.<PluginProperty>of(), callContext);
+ Assert.assertEquals(payment.getAuthAmount().compareTo(requestedAmount), 0);
+ Assert.assertEquals(payment.getCapturedAmount().compareTo(requestedAmount), 0);
+ Assert.assertEquals(payment.getTransactions().size(), 2);
+ Assert.assertNotNull(((DefaultPaymentTransaction) payment.getTransactions().get(0)).getAttemptId());
+ Assert.assertNull(((DefaultPaymentTransaction) payment.getTransactions().get(1)).getAttemptId());
+ }
+
+ @Test(groups = "slow")
+ public void testCreateAuthNoControlCaptureWithControl() throws PaymentApiException {
+ final BigDecimal requestedAmount = BigDecimal.TEN;
+
+ Payment payment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, UUID.randomUUID().toString(),
+ UUID.randomUUID().toString(), ImmutableList.<PluginProperty>of(), callContext);
+ Assert.assertEquals(payment.getAuthAmount().compareTo(requestedAmount), 0);
+ Assert.assertEquals(payment.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getTransactions().size(), 1);
+ Assert.assertNull(((DefaultPaymentTransaction) payment.getTransactions().get(0)).getAttemptId());
+
+ payment = paymentApi.createCaptureWithPaymentControl(account, payment.getId(), payment.getAuthAmount(), payment.getCurrency(), UUID.randomUUID().toString(),
+ ImmutableList.<PluginProperty>of(), PAYMENT_OPTIONS, callContext);
+ Assert.assertEquals(payment.getAuthAmount().compareTo(requestedAmount), 0);
+ Assert.assertEquals(payment.getCapturedAmount().compareTo(requestedAmount), 0);
+ Assert.assertEquals(payment.getTransactions().size(), 2);
+ Assert.assertNull(((DefaultPaymentTransaction) payment.getTransactions().get(0)).getAttemptId());
+ Assert.assertNotNull(((DefaultPaymentTransaction) payment.getTransactions().get(1)).getAttemptId());
}
public static class TestPaymentControlPluginApi implements PaymentControlPluginApi {
public static final String PLUGIN_NAME = "TEST_CONTROL_API_PLUGIN_NAME";
- private final UUID newPaymentMethodId;
+ private UUID newPaymentMethodId;
- public TestPaymentControlPluginApi(final UUID newPaymentMethodId) {
+ public void setNewPaymentMethodId(final UUID newPaymentMethodId) {
this.newPaymentMethodId = newPaymentMethodId;
}
@@ -127,31 +189,4 @@ public class TestPaymentApiWithControl extends PaymentTestSuiteWithEmbeddedDB {
return new DefaultFailureCallResult(null);
}
}
-
- // Verify Payment control API can be used to change the paymentMethodId on the fly and this is reflected in the created Payment.
- @Test(groups = "slow")
- public void testCreateAuthWithControl() throws PaymentApiException {
-
- final BigDecimal requestedAmount = BigDecimal.TEN;
-
- final String paymentExternalKey = "dfdf";
- final String transactionExternalKey = "qwqwqw";
-
- final PaymentOptions paymentOptions = new PaymentOptions() {
- @Override
- public boolean isExternalPayment() {
- return false;
- }
-
- @Override
- public List<String> getPaymentControlPluginNames() {
- return ImmutableList.of(TestPaymentControlPluginApi.PLUGIN_NAME);
- }
- };
-
- final Payment payment = paymentApi.createAuthorizationWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, paymentExternalKey, transactionExternalKey,
- ImmutableList.<PluginProperty>of(), paymentOptions, callContext);
-
- Assert.assertEquals(payment.getPaymentMethodId(), newPaymentMethodId);
- }
}
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentLeavingStateCallback.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentLeavingStateCallback.java
index 1717dc4..1ef93cc 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentLeavingStateCallback.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentLeavingStateCallback.java
@@ -1,8 +1,8 @@
/*
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Groupon licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -121,8 +121,6 @@ public class TestPaymentLeavingStateCallback extends PaymentTestSuiteWithEmbedde
transactions.get(0).getId(), TransactionStatus.SUCCESS, BigDecimal.ONE, Currency.BRL,
"foo", "bar", internalCallContext);
- final InternalCallContext internalCallContextForOtherAccount = new InternalCallContext(paymentStateContext.getInternalCallContext(), 123L);
-
paymentStateContext = new PaymentStateContext(true,
paymentId,
null,
@@ -137,7 +135,7 @@ public class TestPaymentLeavingStateCallback extends PaymentTestSuiteWithEmbedde
paymentStateContext.shouldLockAccountAndDispatch(),
paymentStateContext.getOverridePluginOperationResult(),
paymentStateContext.getProperties(),
- internalCallContextForOtherAccount,
+ internalCallContext,
callContext);
callback.leavingState(state);
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentProcessor.java b/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentProcessor.java
index caa98f1..19f10b9 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentProcessor.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentProcessor.java
@@ -1,8 +1,8 @@
/*
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Groupon licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -27,7 +27,6 @@ import java.util.concurrent.Callable;
import javax.annotation.Nullable;
import org.killbill.billing.account.api.Account;
-import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.events.BusInternalEvent;
import org.killbill.billing.events.PaymentErrorInternalEvent;
@@ -63,7 +62,6 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
@BeforeMethod(groups = "slow")
public void setUp() throws Exception {
account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
- internalCallContext = new InternalCallContext(internalCallContext, 1L);
paymentBusListener = new PaymentBusListener();
eventBus.register(paymentBusListener);
diff --git a/payment/src/test/java/org/killbill/billing/payment/dao/TestDefaultPaymentDao.java b/payment/src/test/java/org/killbill/billing/payment/dao/TestDefaultPaymentDao.java
index 13e8891..5b56ec4 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dao/TestDefaultPaymentDao.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dao/TestDefaultPaymentDao.java
@@ -1,8 +1,8 @@
/*
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Groupon licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -21,6 +21,7 @@ import java.math.BigDecimal;
import java.util.List;
import java.util.UUID;
+import org.killbill.billing.account.api.Account;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.payment.PaymentTestSuiteWithEmbeddedDB;
@@ -36,27 +37,27 @@ public class TestDefaultPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
@Test(groups = "slow")
public void testPaymentCRUD() throws Exception {
for (int i = 0; i < 3; i++) {
- testPaymentCRUDForAccount(UUID.randomUUID(), i + 1);
+ testPaymentCRUDForAccount();
}
}
- public void testPaymentCRUDForAccount(final UUID accountId, final int accountNb) {
- // We need to create specific call contexts to make the account_record_id magic work
- final InternalCallContext accountCallContext = new InternalCallContext(internalCallContext, (long) accountNb);
+ public void testPaymentCRUDForAccount() throws Exception {
+ final Account account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
+ final UUID accountId = account.getId();
final PaymentModelDao specifiedFirstPaymentModelDao = generatePaymentModelDao(accountId);
final PaymentTransactionModelDao specifiedFirstPaymentTransactionModelDao = generatePaymentTransactionModelDao(specifiedFirstPaymentModelDao.getId());
// Create and verify the payment and transaction
- final PaymentModelDao firstPaymentModelDao = paymentDao.insertPaymentWithFirstTransaction(specifiedFirstPaymentModelDao, specifiedFirstPaymentTransactionModelDao, accountCallContext);
+ final PaymentModelDao firstPaymentModelDao = paymentDao.insertPaymentWithFirstTransaction(specifiedFirstPaymentModelDao, specifiedFirstPaymentTransactionModelDao, internalCallContext);
verifyPayment(firstPaymentModelDao, specifiedFirstPaymentModelDao);
- verifyPaymentAndTransactions(accountCallContext, specifiedFirstPaymentModelDao, specifiedFirstPaymentTransactionModelDao);
+ verifyPaymentAndTransactions(internalCallContext, specifiedFirstPaymentModelDao, specifiedFirstPaymentTransactionModelDao);
// Create a second transaction for the same payment
final PaymentTransactionModelDao specifiedSecondPaymentTransactionModelDao = generatePaymentTransactionModelDao(specifiedFirstPaymentModelDao.getId());
- final PaymentTransactionModelDao secondTransactionModelDao = paymentDao.updatePaymentWithNewTransaction(specifiedFirstPaymentTransactionModelDao.getPaymentId(), specifiedSecondPaymentTransactionModelDao, accountCallContext);
+ final PaymentTransactionModelDao secondTransactionModelDao = paymentDao.updatePaymentWithNewTransaction(specifiedFirstPaymentTransactionModelDao.getPaymentId(), specifiedSecondPaymentTransactionModelDao, internalCallContext);
verifyPaymentTransaction(secondTransactionModelDao, specifiedSecondPaymentTransactionModelDao);
- verifyPaymentAndTransactions(accountCallContext, specifiedFirstPaymentModelDao, specifiedFirstPaymentTransactionModelDao, specifiedSecondPaymentTransactionModelDao);
+ verifyPaymentAndTransactions(internalCallContext, specifiedFirstPaymentModelDao, specifiedFirstPaymentTransactionModelDao, specifiedSecondPaymentTransactionModelDao);
// Update the latest transaction
final BigDecimal processedAmount = new BigDecimal("902341.23232");
@@ -74,9 +75,9 @@ public class TestDefaultPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
processedCurrency,
gatewayErrorCode,
gatewayErrorMsg,
- accountCallContext);
+ internalCallContext);
- final PaymentTransactionModelDao updatedSecondPaymentTransactionModelDao = paymentDao.getPaymentTransaction(specifiedSecondPaymentTransactionModelDao.getId(), accountCallContext);
+ final PaymentTransactionModelDao updatedSecondPaymentTransactionModelDao = paymentDao.getPaymentTransaction(specifiedSecondPaymentTransactionModelDao.getId(), internalCallContext);
Assert.assertEquals(updatedSecondPaymentTransactionModelDao.getTransactionStatus(), TransactionStatus.PAYMENT_FAILURE);
Assert.assertEquals(updatedSecondPaymentTransactionModelDao.getGatewayErrorMsg(), gatewayErrorMsg);
Assert.assertEquals(updatedSecondPaymentTransactionModelDao.getGatewayErrorMsg(), gatewayErrorMsg);
@@ -86,14 +87,14 @@ public class TestDefaultPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
final PaymentModelDao paymentModelDao = generatePaymentModelDao(accountId);
final PaymentTransactionModelDao paymentTransactionModelDao = generatePaymentTransactionModelDao(paymentModelDao.getId());
- final PaymentModelDao insertedPaymentModelDao = paymentDao.insertPaymentWithFirstTransaction(paymentModelDao, paymentTransactionModelDao, accountCallContext);
+ final PaymentModelDao insertedPaymentModelDao = paymentDao.insertPaymentWithFirstTransaction(paymentModelDao, paymentTransactionModelDao, internalCallContext);
verifyPayment(insertedPaymentModelDao, paymentModelDao);
// Verify search APIs
Assert.assertEquals(ImmutableList.<PaymentModelDao>copyOf(paymentDao.searchPayments(paymentModelDao.getPaymentMethodId().toString(), 0L, 100L, internalCallContext).iterator()).size(), 1);
Assert.assertEquals(ImmutableList.<PaymentModelDao>copyOf(paymentDao.searchPayments(paymentModelDao.getExternalKey(), 0L, 100L, internalCallContext).iterator()).size(), 1);
}
- Assert.assertEquals(paymentDao.getPaymentsForAccount(specifiedFirstPaymentModelDao.getAccountId(), accountCallContext).size(), 4);
+ Assert.assertEquals(paymentDao.getPaymentsForAccount(specifiedFirstPaymentModelDao.getAccountId(), internalCallContext).size(), 4);
// Verify search APIs
Assert.assertEquals(ImmutableList.<PaymentModelDao>copyOf(paymentDao.searchPayments(accountId.toString(), 0L, 100L, internalCallContext).iterator()).size(), 4);
diff --git a/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java b/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
index 94d024a..8bc6634 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -23,17 +25,16 @@ import java.util.List;
import java.util.UUID;
import org.joda.time.DateTime;
-import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.account.api.Account;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.payment.PaymentTestSuiteWithEmbeddedDB;
import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.payment.api.TransactionStatus;
import org.killbill.billing.payment.api.TransactionType;
import org.killbill.billing.payment.dao.PluginPropertySerializer.PluginPropertySerializerException;
-import org.killbill.billing.util.callcontext.CallOrigin;
-import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.util.callcontext.UserType;
import org.killbill.billing.util.entity.Pagination;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.testng.annotations.Test;
@@ -57,8 +58,6 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
final TransactionType transactionType = TransactionType.AUTHORIZE;
final String pluginName = "superPlugin";
- final UUID accountId = UUID.randomUUID();
-
final List<PluginProperty> properties = new ArrayList<PluginProperty>();
properties.add(new PluginProperty("key1", "value1", false));
properties.add(new PluginProperty("key2", "value2", false));
@@ -286,16 +285,14 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
clock.addDays(1);
final DateTime newTime = clock.getUTCNow();
- final InternalCallContext internalCallContextWithNewTime = new InternalCallContext(InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID, 1687L, UUID.randomUUID(),
- UUID.randomUUID().toString(), CallOrigin.TEST,
- UserType.TEST, "Testing", "This is a test",
- newTime, newTime);
+ internalCallContext.setCreatedDate(newTime);
+ internalCallContext.setUpdatedDate(newTime);
final PaymentTransactionModelDao transaction4 = new PaymentTransactionModelDao(initialTime, initialTime, null, transactionExternalKey4,
paymentModelDao.getId(), TransactionType.AUTHORIZE, newTime,
TransactionStatus.PENDING, BigDecimal.TEN, Currency.AED,
"pending", "");
- paymentDao.updatePaymentWithNewTransaction(paymentModelDao.getId(), transaction4, internalCallContextWithNewTime);
+ paymentDao.updatePaymentWithNewTransaction(paymentModelDao.getId(), transaction4, internalCallContext);
final List<PaymentTransactionModelDao> result = getPendingTransactions(paymentModelDao.getId());
Assert.assertEquals(result.size(), 3);
@@ -314,9 +311,8 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
// Just to guarantee that next clock.getUTCNow() > newTime
try {
Thread.sleep(1000);
- } catch (InterruptedException e) {
+ } catch (final InterruptedException e) {
}
- ;
final Iterable<PaymentTransactionModelDao> transactions2 = paymentDao.getByTransactionStatusAcrossTenants(ImmutableList.of(TransactionStatus.PENDING), clock.getUTCNow(), initialTime, 0L, 1L);
for (PaymentTransactionModelDao paymentTransaction : transactions2) {
@@ -332,10 +328,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
}
@Test(groups = "slow")
- public void testPaymentByStatesAcrossTenants() {
-
- final UUID paymentMethodId = UUID.randomUUID();
- final UUID accountId = UUID.randomUUID();
+ public void testPaymentByStatesAcrossTenants() throws Exception {
final String externalKey1 = "XXhhhhooo1";
final String transactionExternalKey1 = "transactionXX1";
@@ -356,111 +349,73 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
// Right before createdAfterDate, so should not be returned
final DateTime createdDate1 = createdAfterDate.minusHours(1);
- final PaymentModelDao paymentModelDao1 = new PaymentModelDao(createdDate1, createdDate1, accountId, paymentMethodId, externalKey1);
+ clock.setTime(createdDate1);
+ Account account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
+
+ final PaymentModelDao paymentModelDao1 = new PaymentModelDao(createdDate1, createdDate1, account.getId(), account.getPaymentMethodId(), externalKey1);
paymentModelDao1.setStateName("AUTH_ERRORED");
final PaymentTransactionModelDao transaction1 = new PaymentTransactionModelDao(createdDate1, createdDate1, null, transactionExternalKey1,
paymentModelDao1.getId(), TransactionType.AUTHORIZE, createdDate1,
TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
"unknown", "");
- final InternalCallContext context1 = new InternalCallContext(1L,
- 1L,
- internalCallContext.getUserToken(),
- internalCallContext.getCreatedBy(),
- internalCallContext.getCallOrigin(),
- internalCallContext.getContextUserType(),
- internalCallContext.getReasonCode(),
- internalCallContext.getComments(),
- createdDate1,
- createdDate1);
- paymentDao.insertPaymentWithFirstTransaction(paymentModelDao1, transaction1, context1);
+ paymentDao.insertPaymentWithFirstTransaction(paymentModelDao1, transaction1, internalCallContext);
// Right after createdAfterDate, so it should be returned
final DateTime createdDate2 = createdAfterDate.plusHours(1);
- final PaymentModelDao paymentModelDao2 = new PaymentModelDao(createdDate2, createdDate2, accountId, paymentMethodId, externalKey2);
+ clock.setTime(createdDate2);
+ account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
+
+ final PaymentModelDao paymentModelDao2 = new PaymentModelDao(createdDate2, createdDate2, account.getId(), account.getPaymentMethodId(), externalKey2);
paymentModelDao2.setStateName("CAPTURE_ERRORED");
final PaymentTransactionModelDao transaction2 = new PaymentTransactionModelDao(createdDate2, createdDate2, null, transactionExternalKey2,
paymentModelDao2.getId(), TransactionType.AUTHORIZE, createdDate2,
TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
"unknown", "");
- final InternalCallContext context2 = new InternalCallContext(2L,
- 2L,
- internalCallContext.getUserToken(),
- internalCallContext.getCreatedBy(),
- internalCallContext.getCallOrigin(),
- internalCallContext.getContextUserType(),
- internalCallContext.getReasonCode(),
- internalCallContext.getComments(),
- createdDate2,
- createdDate2);
- paymentDao.insertPaymentWithFirstTransaction(paymentModelDao2, transaction2, context2);
+ paymentDao.insertPaymentWithFirstTransaction(paymentModelDao2, transaction2, internalCallContext);
// Right before createdBeforeDate, so it should be returned
final DateTime createdDate3 = createdBeforeDate.minusDays(1);
- final PaymentModelDao paymentModelDao3 = new PaymentModelDao(createdDate3, createdDate3, accountId, paymentMethodId, externalKey3);
+ clock.setTime(createdDate3);
+ account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
+
+ final PaymentModelDao paymentModelDao3 = new PaymentModelDao(createdDate3, createdDate3, account.getId(), account.getPaymentMethodId(), externalKey3);
paymentModelDao3.setStateName("CAPTURE_ERRORED");
final PaymentTransactionModelDao transaction3 = new PaymentTransactionModelDao(createdDate3, createdDate3, null, transactionExternalKey3,
paymentModelDao3.getId(), TransactionType.AUTHORIZE, createdDate3,
TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
"unknown", "");
- final InternalCallContext context3 = new InternalCallContext(3L,
- 3L,
- internalCallContext.getUserToken(),
- internalCallContext.getCreatedBy(),
- internalCallContext.getCallOrigin(),
- internalCallContext.getContextUserType(),
- internalCallContext.getReasonCode(),
- internalCallContext.getComments(),
- createdDate3,
- createdDate3);
-
- paymentDao.insertPaymentWithFirstTransaction(paymentModelDao3, transaction3, context3);
+ paymentDao.insertPaymentWithFirstTransaction(paymentModelDao3, transaction3, internalCallContext);
// Right before createdBeforeDate but with a SUCCESS state so it should NOT be returned
final DateTime createdDate4 = createdBeforeDate.minusDays(1);
- final PaymentModelDao paymentModelDao4 = new PaymentModelDao(createdDate4, createdDate4, accountId, paymentMethodId, externalKey4);
+ clock.setTime(createdDate4);
+ account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
+
+ final PaymentModelDao paymentModelDao4 = new PaymentModelDao(createdDate4, createdDate4, account.getId(), account.getPaymentMethodId(), externalKey4);
paymentModelDao4.setStateName("CAPTURE_SUCCESS");
final PaymentTransactionModelDao transaction4 = new PaymentTransactionModelDao(createdDate4, createdDate4, null, transactionExternalKey4,
paymentModelDao4.getId(), TransactionType.AUTHORIZE, createdDate4,
TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
"unknown", "");
- final InternalCallContext context4 = new InternalCallContext(4L,
- 4L,
- internalCallContext.getUserToken(),
- internalCallContext.getCreatedBy(),
- internalCallContext.getCallOrigin(),
- internalCallContext.getContextUserType(),
- internalCallContext.getReasonCode(),
- internalCallContext.getComments(),
- createdDate4,
- createdDate4);
-
- paymentDao.insertPaymentWithFirstTransaction(paymentModelDao4, transaction4, context4);
+ paymentDao.insertPaymentWithFirstTransaction(paymentModelDao4, transaction4, internalCallContext);
// Right after createdBeforeDate, so it should NOT be returned
final DateTime createdDate5 = createdBeforeDate.plusDays(1);
- final PaymentModelDao paymentModelDao5 = new PaymentModelDao(createdDate5, createdDate5, accountId, paymentMethodId, externalKey5);
+ clock.setTime(createdDate5);
+ account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
+
+ final PaymentModelDao paymentModelDao5 = new PaymentModelDao(createdDate5, createdDate5, account.getId(), account.getPaymentMethodId(), externalKey5);
paymentModelDao5.setStateName("CAPTURE_ERRORED");
final PaymentTransactionModelDao transaction5 = new PaymentTransactionModelDao(createdDate5, createdDate5, null, transactionExternalKey5,
paymentModelDao5.getId(), TransactionType.AUTHORIZE, createdDate5,
TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
"unknown", "");
- final InternalCallContext context5 = new InternalCallContext(5L,
- 5L,
- internalCallContext.getUserToken(),
- internalCallContext.getCreatedBy(),
- internalCallContext.getCallOrigin(),
- internalCallContext.getContextUserType(),
- internalCallContext.getReasonCode(),
- internalCallContext.getComments(),
- createdDate5,
- createdDate5);
-
- paymentDao.insertPaymentWithFirstTransaction(paymentModelDao5, transaction5, context5);
+ paymentDao.insertPaymentWithFirstTransaction(paymentModelDao5, transaction5, internalCallContext);
final String[] errorStates = {"AUTH_ERRORED", "CAPTURE_ERRORED", "REFUND_ERRORED", "CREDIT_ERRORED"};
final List<PaymentModelDao> result = paymentDao.getPaymentsByStatesAcrossTenants(errorStates, createdBeforeDate, createdAfterDate, 10);
@@ -468,32 +423,26 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
}
@Test(groups = "slow")
- public void testPaginationForPaymentByStatesAcrossTenants() {
- // Right before createdAfterDate, so should not be returned
+ public void testPaginationForPaymentByStatesAcrossTenants() throws Exception {
final DateTime createdDate1 = clock.getUTCNow().minusHours(1);
+ clock.setTime(createdDate1);
+
+ final Account account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
final int NB_ENTRIES = 30;
for (int i = 0; i < NB_ENTRIES; i++) {
- final PaymentModelDao paymentModelDao1 = new PaymentModelDao(createdDate1, createdDate1, UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID().toString());
+ final PaymentModelDao paymentModelDao1 = new PaymentModelDao(createdDate1, createdDate1, account.getId(), account.getPaymentMethodId(), UUID.randomUUID().toString());
final PaymentTransactionModelDao transaction1 = new PaymentTransactionModelDao(createdDate1, createdDate1, null, UUID.randomUUID().toString(),
paymentModelDao1.getId(), TransactionType.AUTHORIZE, createdDate1,
TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
"unknown", "");
- final InternalCallContext context1 = new InternalCallContext(1L,
- 1L,
- internalCallContext.getUserToken(),
- internalCallContext.getCreatedBy(),
- internalCallContext.getCallOrigin(),
- internalCallContext.getContextUserType(),
- internalCallContext.getReasonCode(),
- internalCallContext.getComments(),
- createdDate1,
- createdDate1);
- paymentDao.insertPaymentWithFirstTransaction(paymentModelDao1, transaction1, context1);
+ paymentDao.insertPaymentWithFirstTransaction(paymentModelDao1, transaction1, internalCallContext);
}
- final Pagination<PaymentTransactionModelDao> result = paymentDao.getByTransactionStatusAcrossTenants(ImmutableList.of(TransactionStatus.UNKNOWN), clock.getUTCNow(), createdDate1, 0L, new Long(NB_ENTRIES));
+ clock.setTime(createdDate1.plusHours(1));
+
+ final Pagination<PaymentTransactionModelDao> result = paymentDao.getByTransactionStatusAcrossTenants(ImmutableList.of(TransactionStatus.UNKNOWN), clock.getUTCNow(), createdDate1, 0L, (long) NB_ENTRIES);
Assert.assertEquals(result.getTotalNbRecords(), new Long(NB_ENTRIES));
final Iterator<PaymentTransactionModelDao> iterator = result.iterator();
@@ -505,10 +454,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
}
@Test(groups = "slow")
- public void testPaymentAttemptsByStateAcrossTenants() {
-
- final UUID paymentMethodId = UUID.randomUUID();
- final UUID accountId = UUID.randomUUID();
+ public void testPaymentAttemptsByStateAcrossTenants() throws Exception {
final String externalKey1 = "gfhfg";
final String transactionExternalKey1 = "sadas";
@@ -521,56 +467,41 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
final String stateName = "FOO";
final String pluginName = "miraculous";
- final PaymentAttemptModelDao attempt1 = new PaymentAttemptModelDao(accountId, paymentMethodId, createdAfterDate, createdAfterDate, externalKey1,
+ clock.setTime(createdAfterDate);
+ Account account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
+
+ final PaymentAttemptModelDao attempt1 = new PaymentAttemptModelDao(account.getId(), account.getPaymentMethodId(), createdAfterDate, createdAfterDate, externalKey1,
UUID.randomUUID(), transactionExternalKey1, TransactionType.AUTHORIZE, stateName, BigDecimal.ONE, Currency.USD,
ImmutableList.<String>of(pluginName), null);
- final PaymentAttemptModelDao attempt2 = new PaymentAttemptModelDao(accountId, paymentMethodId, createdAfterDate, createdAfterDate, externalKey2,
+ paymentDao.insertPaymentAttemptWithProperties(attempt1, internalCallContext);
+
+ account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
+
+ final PaymentAttemptModelDao attempt2 = new PaymentAttemptModelDao(account.getId(), account.getPaymentMethodId(), createdAfterDate, createdAfterDate, externalKey2,
UUID.randomUUID(), transactionExternalKey2, TransactionType.AUTHORIZE, stateName, BigDecimal.ONE, Currency.USD,
ImmutableList.<String>of(pluginName), null);
- final InternalCallContext context1 = new InternalCallContext(1L,
- 1L,
- internalCallContext.getUserToken(),
- internalCallContext.getCreatedBy(),
- internalCallContext.getCallOrigin(),
- internalCallContext.getContextUserType(),
- internalCallContext.getReasonCode(),
- internalCallContext.getComments(),
- createdAfterDate,
- createdAfterDate);
- paymentDao.insertPaymentAttemptWithProperties(attempt1, context1);
-
- final InternalCallContext context2 = new InternalCallContext(2L,
- 2L,
- internalCallContext.getUserToken(),
- internalCallContext.getCreatedBy(),
- internalCallContext.getCallOrigin(),
- internalCallContext.getContextUserType(),
- internalCallContext.getReasonCode(),
- internalCallContext.getComments(),
- createdAfterDate,
- createdAfterDate);
- paymentDao.insertPaymentAttemptWithProperties(attempt2, context2);
+ paymentDao.insertPaymentAttemptWithProperties(attempt2, internalCallContext);
final Pagination<PaymentAttemptModelDao> result = paymentDao.getPaymentAttemptsByStateAcrossTenants(stateName, createdBeforeDate, 0L, 2L);
Assert.assertEquals(result.getTotalNbRecords().longValue(), 2L);
}
@Test(groups = "slow")
- public void testUpdatePaymentAttempt() throws PluginPropertySerializerException {
+ public void testUpdatePaymentAttempt() throws Exception {
+ final DateTime createdAfterDate = clock.getUTCNow().minusDays(10);
+ clock.setTime(createdAfterDate);
+
+ final Account account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
- final UUID paymentMethodId = UUID.randomUUID();
- final UUID accountId = UUID.randomUUID();
final String externalKey1 = "2354";
final String transactionExternalKey1 = "jkjkjk";
- final DateTime createdAfterDate = clock.getUTCNow().minusDays(10);
-
final String stateName = "RRRRR";
final String pluginName = "elated";
- final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(accountId, paymentMethodId, createdAfterDate, createdAfterDate, externalKey1,
+ final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(account.getId(), account.getPaymentMethodId(), createdAfterDate, createdAfterDate, externalKey1,
UUID.randomUUID(), transactionExternalKey1, TransactionType.AUTHORIZE, stateName, BigDecimal.ONE, Currency.USD,
ImmutableList.<String>of(pluginName), null);
@@ -588,7 +519,6 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
properties.add(new PluginProperty("prop1", "value1", false));
properties.add(new PluginProperty("prop2", "value2", false));
-
final byte [] serializedProperties = PluginPropertySerializer.serialize(properties);
paymentDao.updatePaymentAttemptWithProperties(rehydratedAttempt.getId(), transactionId, newStateName, serializedProperties, internalCallContext);
final PaymentAttemptModelDao attempt2 = paymentDao.getPaymentAttempt(rehydratedAttempt.getId(), internalCallContext);
@@ -598,7 +528,6 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
final Iterable<PluginProperty> properties2 = PluginPropertySerializer.deserialize(attempt2.getPluginProperties());
checkProperty(properties2, new PluginProperty("prop1", "value1", false));
checkProperty(properties2, new PluginProperty("prop2", "value2", false));
-
}
private void checkProperty(final Iterable<PluginProperty> properties, final PluginProperty expected) {
diff --git a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
index 14072c3..dc7b36f 100644
--- a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
@@ -34,6 +34,7 @@ import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.bus.api.PersistentBus;
import org.killbill.commons.profiling.Profiling;
import org.testng.annotations.AfterMethod;
@@ -73,6 +74,8 @@ public abstract class PaymentTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
protected TestPaymentHelper testHelper;
@Inject
protected PaymentExecutors paymentExecutors;
+ @Inject
+ protected NonEntityDao nonEntityDao;
@Override
protected KillbillConfigSource getConfigSource() {
diff --git a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
index 8788e73..cc8c6e1 100644
--- a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
+++ b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -27,6 +27,9 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.annotation.Nullable;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.payment.api.PaymentMethodPlugin;
@@ -46,6 +49,7 @@ import org.killbill.billing.util.entity.DefaultPagination;
import org.killbill.billing.util.entity.Pagination;
import org.killbill.clock.Clock;
+import com.google.common.base.MoreObjects;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
@@ -68,6 +72,8 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
private final AtomicBoolean makeNextInvoiceFailWithException = new AtomicBoolean(false);
private final AtomicBoolean makeAllInvoicesFailWithError = new AtomicBoolean(false);
private final AtomicInteger makePluginWaitSomeMilliseconds = new AtomicInteger(0);
+ private final AtomicReference<BigDecimal> overrideNextProcessedAmount = new AtomicReference<BigDecimal>();
+ private final AtomicReference<Currency> overrideNextProcessedCurrency = new AtomicReference<Currency>();
private final Map<String, InternalPaymentInfo> payments = new ConcurrentHashMap<String, InternalPaymentInfo>();
private final Map<String, List<PaymentTransactionInfoPlugin>> paymentTransactions = new ConcurrentHashMap<String, List<PaymentTransactionInfoPlugin>>();
@@ -192,6 +198,7 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
makeAllInvoicesFailWithError.set(false);
makeNextInvoiceFailWithError.set(false);
makePluginWaitSomeMilliseconds.set(0);
+ overrideNextProcessedAmount.set(null);
paymentMethods.clear();
payments.clear();
paymentTransactions.clear();
@@ -214,6 +221,14 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
makePluginWaitSomeMilliseconds.set(milliseconds);
}
+ public void overrideNextProcessedAmount(final BigDecimal amount) {
+ overrideNextProcessedAmount.set(amount);
+ }
+
+ public void overrideNextProcessedCurrency(final Currency currency) {
+ overrideNextProcessedCurrency.set(currency);
+ }
+
public void updatePaymentTransactions(final UUID paymentId, final List<PaymentTransactionInfoPlugin> newTransactions) {
if (paymentTransactions.containsKey(paymentId.toString())) {
paymentTransactions.put (paymentId.toString(), newTransactions);
@@ -343,7 +358,7 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
return getPaymentTransactionInfoPluginResult(kbPaymentId, kbTransactionId, TransactionType.REFUND, refundAmount, currency, properties);
}
- private PaymentTransactionInfoPlugin getPaymentTransactionInfoPluginResult(final UUID kbPaymentId, final UUID kbTransactionId, final TransactionType type, final BigDecimal amount, final Currency currency, final Iterable<PluginProperty> pluginProperties) throws PaymentPluginApiException {
+ private PaymentTransactionInfoPlugin getPaymentTransactionInfoPluginResult(final UUID kbPaymentId, final UUID kbTransactionId, final TransactionType type, final BigDecimal amount, @Nullable final Currency currency, final Iterable<PluginProperty> pluginProperties) throws PaymentPluginApiException {
if (makePluginWaitSomeMilliseconds.get() > 0) {
try {
Thread.sleep(makePluginWaitSomeMilliseconds.get());
@@ -379,7 +394,13 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
payments.put(kbPaymentId.toString(), info);
}
- final PaymentTransactionInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, type, amount, currency, clock.getUTCNow(), clock.getUTCNow(), status, errorCode, error);
+ final BigDecimal processedAmount = MoreObjects.firstNonNull(overrideNextProcessedAmount.getAndSet(null), amount);
+ Currency processedCurrency = overrideNextProcessedCurrency.getAndSet(null);
+ if (processedCurrency == null) {
+ processedCurrency = currency;
+ }
+
+ final PaymentTransactionInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, type, processedAmount, processedCurrency, clock.getUTCNow(), clock.getUTCNow(), status, errorCode, error);
List<PaymentTransactionInfoPlugin> existingTransactions = paymentTransactions.get(kbPaymentId.toString());
if (existingTransactions == null) {
existingTransactions = new ArrayList<PaymentTransactionInfoPlugin>();
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
index 0ba6343..ccf0a1c 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
@@ -288,7 +288,6 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
testListener.assertListenerStatus();
// Artificially move the transaction status to UNKNOWN
- final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
final String paymentStateName = paymentSMHelper.getErroredStateForTransaction(TransactionType.AUTHORIZE).toString();
testListener.pushExpectedEvent(NextEvent.PAYMENT_PLUGIN_ERROR);
paymentDao.updatePaymentAndTransactionOnCompletion(account.getId(), payment.getId(), TransactionType.AUTHORIZE, paymentStateName, paymentStateName,
@@ -321,7 +320,6 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
testListener.assertListenerStatus();
// Artificially move the transaction status to UNKNOWN
- final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
final String paymentStateName = paymentSMHelper.getErroredStateForTransaction(TransactionType.AUTHORIZE).toString();
testListener.pushExpectedEvent(NextEvent.PAYMENT_PLUGIN_ERROR);
paymentDao.updatePaymentAndTransactionOnCompletion(account.getId(), payment.getId(), TransactionType.AUTHORIZE, paymentStateName, paymentStateName,
@@ -367,8 +365,6 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
final Payment payment = paymentApi.getPaymentByExternalKey(paymentExternalKey, false, ImmutableList.<PluginProperty>of(), callContext);
// Artificially move the transaction status to UNKNOWN
- final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-
final String paymentStateName = paymentSMHelper.getErroredStateForTransaction(TransactionType.AUTHORIZE).toString();
testListener.pushExpectedEvent(NextEvent.PAYMENT_PLUGIN_ERROR);
paymentDao.updatePaymentAndTransactionOnCompletion(account.getId(), payment.getId(), TransactionType.AUTHORIZE, paymentStateName, paymentStateName,
@@ -401,8 +397,6 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
transactionExternalKey, ImmutableList.<PluginProperty>of(), callContext);
testListener.assertListenerStatus();
- final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-
// Artificially move the transaction status to PENDING
final String paymentStateName = paymentSMHelper.getPendingStateForTransaction(TransactionType.AUTHORIZE).toString();
testListener.pushExpectedEvent(NextEvent.PAYMENT);
@@ -434,8 +428,6 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
transactionExternalKey, ImmutableList.<PluginProperty>of(), callContext);
testListener.assertListenerStatus();
- final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-
// Artificially move the transaction status to PENDING AND update state on the plugin as well
final List<PaymentTransactionInfoPlugin> paymentTransactions = mockPaymentProviderPlugin.getPaymentInfo(account.getId(), payment.getId(), ImmutableList.<PluginProperty>of(), callContext);
final PaymentTransactionInfoPlugin oTx = paymentTransactions.remove(0);
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java b/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java
index 2aaa167..0c47d84 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -20,25 +20,32 @@ package org.killbill.billing.payment;
import java.util.UUID;
+import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
+import org.killbill.billing.GuicyKillbillTestSuite;
+import org.killbill.billing.GuicyKillbillTestSuiteNoDB;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountInternalApi;
import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.account.api.ImmutableAccountInternalApi;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.callcontext.MutableInternalCallContext;
import org.killbill.billing.catalog.api.Currency;
-import org.killbill.billing.dao.MockNonEntityDao;
import org.killbill.billing.events.InvoiceCreationInternalEvent;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceInternalApi;
import org.killbill.billing.invoice.api.InvoiceItem;
+import org.killbill.billing.mock.MockAccountBuilder;
import org.killbill.billing.payment.api.PaymentApi;
import org.killbill.billing.payment.api.PaymentMethodPlugin;
import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.payment.provider.DefaultNoOpPaymentMethodPlugin;
import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.bus.api.PersistentBus;
import org.killbill.bus.api.PersistentBus.EventBusException;
import org.killbill.clock.Clock;
@@ -51,32 +58,38 @@ public class TestPaymentHelper {
protected final AccountUserApi accountApi;
protected final AccountInternalApi accountInternalApi;
+ protected final ImmutableAccountInternalApi immutableAccountInternalApi;
protected final InvoiceInternalApi invoiceApi;
- protected PaymentApi paymentApi;
private final PersistentBus eventBus;
private final Clock clock;
- private final MockNonEntityDao mockNonEntityDao;
- private final InternalCallContext internalCallContext;
+ private final NonEntityDao nonEntityDao;
+ private final MutableInternalCallContext internalCallContext;
+ private final InternalCallContextFactory internalCallContextFactory;
private final CallContext context;
+ protected PaymentApi paymentApi;
@Inject
public TestPaymentHelper(final AccountUserApi accountApi,
final AccountInternalApi accountInternalApi,
+ final ImmutableAccountInternalApi immutableAccountInternalApi,
final InvoiceInternalApi invoiceApi,
final PaymentApi paymentApi,
final PersistentBus eventBus,
final Clock clock,
- final MockNonEntityDao mockNonEntityDao,
- final InternalCallContext internalCallContext,
+ final NonEntityDao nonEntityDao,
+ final MutableInternalCallContext internalCallContext,
+ final InternalCallContextFactory internalCallContextFactory,
final CallContext context) {
this.accountApi = accountApi;
this.eventBus = eventBus;
this.accountInternalApi = accountInternalApi;
+ this.immutableAccountInternalApi = immutableAccountInternalApi;
this.invoiceApi = invoiceApi;
this.paymentApi = paymentApi;
this.clock = clock;
- this.mockNonEntityDao = mockNonEntityDao;
+ this.nonEntityDao = nonEntityDao;
this.internalCallContext = internalCallContext;
+ this.internalCallContextFactory = internalCallContextFactory;
this.context = context;
}
@@ -130,18 +143,18 @@ public class TestPaymentHelper {
Mockito.when(accountData.getBillCycleDayLocal()).thenReturn(1);
Mockito.when(accountData.isMigrated()).thenReturn(false);
Mockito.when(accountData.isNotifiedForInvoices()).thenReturn(false);
+ Mockito.when(accountData.getTimeZone()).thenReturn(DateTimeZone.UTC);
+ Mockito.when(accountData.getCreatedDate()).thenReturn(clock.getUTCNow());
Account account;
if (isFastTest()) {
- account = accountData;
- Mockito.when(accountInternalApi.getAccountById(Mockito.<UUID>any(), Mockito.<InternalTenantContext>any())).thenReturn(account);
- Mockito.when(accountInternalApi.getAccountByKey(Mockito.anyString(), Mockito.<InternalTenantContext>any())).thenReturn(account);
- mockNonEntityDao.addTenantRecordIdMapping(account.getId(), internalCallContext);
- mockNonEntityDao.addAccountRecordIdMapping(account.getId(), internalCallContext);
+ account = GuicyKillbillTestSuiteNoDB.createMockAccount(accountData, accountApi, accountInternalApi, immutableAccountInternalApi, nonEntityDao, clock, internalCallContextFactory, context, internalCallContext);
} else {
account = accountApi.createAccount(accountData, context);
}
+ GuicyKillbillTestSuite.refreshCallContext(account.getId(), clock, internalCallContextFactory, context, internalCallContext);
+
if (addPaymentMethod) {
final PaymentMethodPlugin pm = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), true, null);
account = addTestPaymentMethod(account, pm);
@@ -151,10 +164,15 @@ public class TestPaymentHelper {
}
public Account addTestPaymentMethod(final Account account, final PaymentMethodPlugin paymentMethodInfo) throws Exception {
- final UUID paymentMethodId = paymentApi.addPaymentMethod(account, paymentMethodInfo.getExternalPaymentMethodId(), MockPaymentProviderPlugin.PLUGIN_NAME, true, paymentMethodInfo, ImmutableList.<PluginProperty>of(), context);
+ return addTestPaymentMethod(account, paymentMethodInfo, ImmutableList.<PluginProperty>of());
+ }
+
+ public Account addTestPaymentMethod(final Account account, final PaymentMethodPlugin paymentMethodInfo, final Iterable<PluginProperty> pluginProperties) throws Exception {
+ final UUID paymentMethodId = paymentApi.addPaymentMethod(account, paymentMethodInfo.getExternalPaymentMethodId(), MockPaymentProviderPlugin.PLUGIN_NAME, true, paymentMethodInfo, pluginProperties, context);
if (isFastTest()) {
- Mockito.when(account.getPaymentMethodId()).thenReturn(paymentMethodId);
- return account;
+ final Account account1 = new MockAccountBuilder(account).paymentMethodId(paymentMethodId).build();
+ accountApi.updateAccount(account, context);
+ return account1;
} else {
// To reflect the payment method id change
return accountApi.getAccountById(account.getId(), context);
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java b/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java
index f289f57..b5462f5 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -75,7 +75,6 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
mockPaymentProviderPlugin.clear();
retryService.initialize();
retryService.start();
-
}
@Override
profiles/killbill/pom.xml 6(+6 -0)
diff --git a/profiles/killbill/pom.xml b/profiles/killbill/pom.xml
index a81d4b9..761ab45 100644
--- a/profiles/killbill/pom.xml
+++ b/profiles/killbill/pom.xml
@@ -308,6 +308,12 @@
</dependency>
<dependency>
<groupId>org.kill-bill.commons</groupId>
+ <artifactId>killbill-queue</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.kill-bill.commons</groupId>
<artifactId>killbill-skeleton</artifactId>
</dependency>
<dependency>
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/security/TenantFilter.java b/profiles/killbill/src/main/java/org/killbill/billing/server/security/TenantFilter.java
index 3c6c6ce..7f2a2bb 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/security/TenantFilter.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/security/TenantFilter.java
@@ -131,6 +131,8 @@ public class TenantFilter implements Filter {
isTenantCreationRequest(path, httpMethod) ||
// Retrieve user permissions should not require tenant info since this is cross tenants
isPermissionRequest(path, httpMethod) ||
+ // Node request are cross tenant
+ isNodeCreationRequest(path, httpMethod) ||
// Metrics servlets
isMetricsRequest(path, httpMethod) ||
// See KillBillShiroWebModule#CorsBasicHttpAuthenticationFilter
@@ -157,6 +159,10 @@ public class TenantFilter implements Filter {
return JaxrsResource.TENANTS_PATH.equals(path) && "POST".equals(httpMethod);
}
+ private boolean isNodeCreationRequest(final String path, final String httpMethod) {
+ return JaxrsResource.NODES_INFO_PATH.equals(path) && "POST".equals(httpMethod);
+ }
+
private boolean isMetricsRequest(final String path, final String httpMethod) {
return KillbillGuiceListener.METRICS_SERVLETS_PATHS.contains(path) && "GET".equals(httpMethod);
}
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestBundle.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestBundle.java
index e9bc2be..fbc8a31 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestBundle.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestBundle.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -74,12 +74,7 @@ public class TestBundle extends TestJaxrsBase {
public void testBundleNonExistent() throws Exception {
final Account accountJson = createAccount();
- try {
- killBillClient.getBundle(UUID.randomUUID());
- Assert.fail();
- } catch (final KillBillClientException e) {
- Assert.assertEquals(e.getBillingException().getClassName(), "org.killbill.billing.util.callcontext.InternalCallContextFactory$ObjectDoesNotExist");
- }
+ Assert.assertNull(killBillClient.getBundle(UUID.randomUUID()));
Assert.assertTrue(killBillClient.getAccountBundles(accountJson.getAccountId(), "98374982743892").isEmpty());
Assert.assertTrue(killBillClient.getAccountBundles(accountJson.getAccountId()).isEmpty());
}
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCredit.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCredit.java
index 4ccc863..e323ac2 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCredit.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCredit.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -26,8 +26,6 @@ import org.killbill.billing.client.KillBillClientException;
import org.killbill.billing.client.model.Account;
import org.killbill.billing.client.model.Credit;
import org.killbill.billing.client.model.Invoice;
-import org.killbill.billing.invoice.api.InvoiceApiException;
-import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -53,6 +51,7 @@ public class TestCredit extends TestJaxrsBase {
final Credit credit = new Credit();
credit.setAccountId(accountJson.getAccountId());
credit.setCreditAmount(creditAmount);
+ credit.setDescription("description");
Credit objFromJson = killBillClient.createCredit(credit, false, createdBy, reason, comment);
final UUID invoiceId = objFromJson.getInvoiceId();
@@ -64,6 +63,7 @@ public class TestCredit extends TestJaxrsBase {
assertEquals(objFromJson.getInvoiceId(), invoiceId);
assertEquals(objFromJson.getCreditAmount().compareTo(creditAmount), 0);
assertEquals(objFromJson.getEffectiveDate().compareTo(effectiveDate.toLocalDate()), 0);
+ assertEquals(objFromJson.getDescription().compareTo("description"), 0);
}
@Test(groups = "slow", description = "Can add a credit to an existing account",
@@ -106,11 +106,6 @@ public class TestCredit extends TestJaxrsBase {
@Test(groups = "slow", description = "Cannot retrieve a non existing credit")
public void testCreditDoesNotExist() throws Exception {
- try {
- killBillClient.getCredit(UUID.randomUUID());
- Assert.fail();
- } catch (final KillBillClientException e) {
- Assert.assertEquals(e.getBillingException().getClassName(), "org.killbill.billing.util.callcontext.InternalCallContextFactory$ObjectDoesNotExist");
- }
+ assertNull(killBillClient.getCredit(UUID.randomUUID()));
}
}
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java
index c795c16..080d812 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java
@@ -145,26 +145,11 @@ public class TestEntitlement extends TestJaxrsBase {
subscription.setBillingPeriod(BillingPeriod.ANNUAL);
subscription.setPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
- try {
- killBillClient.updateSubscription(subscription, CALL_COMPLETION_TIMEOUT_SEC, createdBy, reason, comment);
- Assert.fail();
- } catch (final KillBillClientException e) {
- Assert.assertEquals(e.getBillingException().getClassName(), "org.killbill.billing.util.callcontext.InternalCallContextFactory$ObjectDoesNotExist");
- }
-
- try {
- killBillClient.cancelSubscription(subscriptionId, createdBy, reason, comment);
- Assert.fail();
- } catch (final KillBillClientException e) {
- Assert.assertEquals(e.getBillingException().getClassName(), "org.killbill.billing.util.callcontext.InternalCallContextFactory$ObjectDoesNotExist");
- }
-
- try {
- killBillClient.getSubscription(subscriptionId);
- Assert.fail();
- } catch (final KillBillClientException e) {
- Assert.assertEquals(e.getBillingException().getClassName(), "org.killbill.billing.util.callcontext.InternalCallContextFactory$ObjectDoesNotExist");
- }
+ assertNull(killBillClient.updateSubscription(subscription, CALL_COMPLETION_TIMEOUT_SEC, createdBy, reason, comment));
+
+ killBillClient.cancelSubscription(subscriptionId, createdBy, reason, comment);
+
+ assertNull(killBillClient.getSubscription(subscriptionId));
}
@Test(groups = "slow", description = "Can override billing policy on change")
@@ -221,7 +206,7 @@ public class TestEntitlement extends TestJaxrsBase {
final Subscription subscription = killBillClient.createSubscription(input, DEFAULT_WAIT_COMPLETION_TIMEOUT_SEC, createdBy, reason, comment);
- final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false, AuditLevel.FULL);
+ final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false, false, AuditLevel.FULL);
assertEquals(invoices.size(), 1);
assertEquals(invoices.get(0).getAmount().compareTo(BigDecimal.TEN), 0);
}
@@ -261,12 +246,12 @@ public class TestEntitlement extends TestJaxrsBase {
subscriptions.add(base);
subscriptions.add(addOn1);
subscriptions.add(addOn2);
- final Bundle bundle = killBillClient.createSubscriptionWithAddOns(subscriptions, initialDate, 10, "createdBy", "", "");
+ final Bundle bundle = killBillClient.createSubscriptionWithAddOns(subscriptions, initialDate.toLocalDate(), 10, "createdBy", "", "");
assertNotNull(bundle);
assertEquals(bundle.getExternalKey(), "base");
assertEquals(bundle.getSubscriptions().size(), 3);
- final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false, AuditLevel.FULL);
+ final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false, false, AuditLevel.FULL);
assertEquals(invoices.size(), 1);
}
@@ -283,7 +268,7 @@ public class TestEntitlement extends TestJaxrsBase {
input.setProductCategory(ProductCategory.BASE);
input.setBillingPeriod(BillingPeriod.MONTHLY);
input.setPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
- final Subscription entitlementJson = killBillClient.createSubscription(input, initialDate.plusMonths(1), -1, createdBy, reason, comment);
+ final Subscription entitlementJson = killBillClient.createSubscription(input, initialDate.toLocalDate().plusMonths(1), -1, createdBy, reason, comment);
Assert.assertEquals(entitlementJson.getProductName(), input.getProductName());
Assert.assertEquals(entitlementJson.getProductCategory(), input.getProductCategory());
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
index 6580618..a9c6129 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -43,12 +43,14 @@ import org.killbill.billing.client.model.PaymentMethod;
import org.killbill.billing.entitlement.api.SubscriptionEventType;
import org.killbill.billing.invoice.api.DryRunType;
import org.killbill.billing.invoice.api.InvoiceStatus;
+import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.payment.provider.ExternalPaymentProviderPlugin;
import org.killbill.billing.util.api.AuditLevel;
import org.testng.Assert;
import org.testng.annotations.Test;
import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import static org.testng.Assert.assertEquals;
@@ -64,7 +66,7 @@ public class TestInvoice extends TestJaxrsBase {
final Account accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
- final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false, AuditLevel.FULL);
+ final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false, false, AuditLevel.FULL);
assertEquals(invoices.size(), 2);
for (final Invoice invoiceJson : invoices) {
Assert.assertEquals(invoiceJson.getAuditLogs().size(), 1);
@@ -93,6 +95,105 @@ public class TestInvoice extends TestJaxrsBase {
final Invoice firstInvoiceByNumberJson = killBillClient.getInvoice(invoiceJson.getInvoiceNumber());
assertEquals(firstInvoiceByNumberJson, invoiceJson);
+ // Check we can retrieve the HTML version
+ final String htmlInvoice = killBillClient.getInvoiceAsHtml(invoiceJson.getInvoiceId());
+ assertEquals(htmlInvoice, "<html>\n" +
+ " <head>\n" +
+ " <style type=\"text/css\">\n" +
+ " th {align=left; width=225px; border-bottom: solid 2px black;}\n" +
+ " </style>\n" +
+ " </head>\n" +
+ " <body>\n" +
+ " <h1>invoiceTitle</h1>\n" +
+ " <table>\n" +
+ " <tr>\n" +
+ " <td rowspan=3 width=350px>Insert image here</td>\n" +
+ " <td width=100px/>\n" +
+ " <td width=225px/>\n" +
+ " <td width=225px/>\n" +
+ " </tr>\n" +
+ " <tr>\n" +
+ " <td />\n" +
+ " <td align=right>invoiceDate</td>\n" +
+ " <td>25 avr. 2012</td>\n" +
+ " </tr>\n" +
+ " <tr>\n" +
+ " <td />\n" +
+ " <td align=right>invoiceNumber</td>\n" +
+ " <td>1</td>\n" +
+ " </tr>\n" +
+ " <tr>\n" +
+ " <td>companyName</td>\n" +
+ " <td></td>\n" +
+ " <td align=right>accountOwnerName</td>\n" +
+ " <td>" + accountJson.getName() + "</td>\n" +
+ " </tr>\n" +
+ " <tr>\n" +
+ " <td>companyAddress</td>\n" +
+ " <td />\n" +
+ " <td />\n" +
+ " <td>" + accountJson.getEmail() + "</td>\n" +
+ " </tr>\n" +
+ " <tr>\n" +
+ " <td>companyCityProvincePostalCode</td>\n" +
+ " <td />\n" +
+ " <td />\n" +
+ " <td>" + accountJson.getPhone() + "</td>\n" +
+ " </tr>\n" +
+ " <tr>\n" +
+ " <td>companyCountry</td>\n" +
+ " <td />\n" +
+ " <td />\n" +
+ " <td />\n" +
+ " </tr>\n" +
+ " <tr>\n" +
+ " <td><companyUrl</td>\n" +
+ " <td />\n" +
+ " <td />\n" +
+ " <td />\n" +
+ " </tr>\n" +
+ " </table>\n" +
+ " <br />\n" +
+ " <br />\n" +
+ " <br />\n" +
+ " <table>\n" +
+ " <tr>\n" +
+ " <th>invoiceItemBundleName</td>\n" +
+ " <th>invoiceItemDescription</td>\n" +
+ " <th>invoiceItemServicePeriod</td>\n" +
+ " <th>invoiceItemAmount</td>\n" +
+ " </tr>\n" +
+ " \n" +
+ " <tr>\n" +
+ " <td>shotgun-monthly-trial</td>\n" +
+ " <td>Monthly shotgun plan</td>\n" +
+ " <td>25 avr. 2012</td>\n" +
+ " <td>USD 0E-9</td>\n" +
+ " </tr>\n" +
+ " \n" +
+ " <tr>\n" +
+ " <td colspan=4 />\n" +
+ " </tr>\n" +
+ " <tr>\n" +
+ " <td colspan=2 />\n" +
+ " <td align=right><strong>invoiceAmount</strong></td>\n" +
+ " <td align=right><strong>0.00</strong></td>\n" +
+ " </tr>\n" +
+ " <tr>\n" +
+ " <td colspan=2 />\n" +
+ " <td align=right><strong>invoiceAmountPaid</strong></td>\n" +
+ " <td align=right><strong>0</strong></td>\n" +
+ " </tr>\n" +
+ " <tr>\n" +
+ " <td colspan=2 />\n" +
+ " <td align=right><strong>invoiceBalance</strong></td>\n" +
+ " <td align=right><strong>0.00</strong></td>\n" +
+ " </tr>\n" +
+ " </table>\n" +
+ " </body>\n" +
+ "</html>\n" +
+ "\n");
+
// Then create a dryRun for next upcoming invoice
final InvoiceDryRun dryRunArg = new InvoiceDryRun(DryRunType.UPCOMING_INVOICE, null,
null, null, null, null, null, null, null, null, null, null);
@@ -241,7 +342,7 @@ public class TestInvoice extends TestJaxrsBase {
final Account accountJson = createAccountNoPMBundleAndSubscriptionAndWaitForFirstInvoice();
// Get the invoices
- final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true);
+ final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false);
// 2 invoices but look for the non zero dollar one
assertEquals(invoices.size(), 2);
final Invoice invoice = invoices.get(1);
@@ -302,7 +403,7 @@ public class TestInvoice extends TestJaxrsBase {
final Account accountJson = createAccountNoPMBundleAndSubscriptionAndWaitForFirstInvoice();
// Get the invoices
- final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true);
+ final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false);
// 2 invoices but look for the non zero dollar one
assertEquals(invoices.size(), 2);
final Invoice invoice = invoices.get(1);
@@ -342,7 +443,7 @@ public class TestInvoice extends TestJaxrsBase {
externalCharge.setAmount(chargeAmount);
externalCharge.setCurrency(Currency.valueOf(accountJson.getCurrency()));
externalCharge.setDescription(UUID.randomUUID().toString());
- final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), false, true, createdBy, reason, comment);
+ final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCToday(), false, true, createdBy, reason, comment);
final Invoice invoiceWithItems = killBillClient.getInvoice(createdExternalCharge.getInvoiceId(), true);
assertEquals(invoiceWithItems.getBalance().compareTo(chargeAmount), 0);
assertEquals(invoiceWithItems.getItems().size(), 1);
@@ -379,7 +480,7 @@ public class TestInvoice extends TestJaxrsBase {
externalCharge2.setDescription(UUID.randomUUID().toString());
externalCharges.add(externalCharge2);
- final List<InvoiceItem> createdExternalCharges = killBillClient.createExternalCharges(externalCharges, clock.getUTCNow(), false, true, createdBy, reason, comment);
+ final List<InvoiceItem> createdExternalCharges = killBillClient.createExternalCharges(externalCharges, clock.getUTCToday(), false, true, createdBy, reason, comment);
assertEquals(createdExternalCharges.size(), 2);
assertEquals(createdExternalCharges.get(0).getCurrency().toString(), accountJson.getCurrency());
assertEquals(createdExternalCharges.get(1).getCurrency().toString(), accountJson.getCurrency());
@@ -401,7 +502,7 @@ public class TestInvoice extends TestJaxrsBase {
externalCharge.setAccountId(accountJson.getAccountId());
externalCharge.setAmount(chargeAmount);
externalCharge.setCurrency(Currency.valueOf(accountJson.getCurrency()));
- final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), true, true, createdBy, reason, comment);
+ final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCToday(), true, true, createdBy, reason, comment);
final Invoice invoiceWithItems = killBillClient.getInvoice(createdExternalCharge.getInvoiceId(), true);
assertEquals(invoiceWithItems.getBalance().compareTo(BigDecimal.ZERO), 0);
assertEquals(invoiceWithItems.getItems().size(), 1);
@@ -426,7 +527,7 @@ public class TestInvoice extends TestJaxrsBase {
externalCharge.setAmount(chargeAmount);
externalCharge.setCurrency(Currency.valueOf(accountJson.getCurrency()));
externalCharge.setBundleId(bundleId);
- final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), false, true, createdBy, reason, comment);
+ final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCToday(), false, true, createdBy, reason, comment);
final Invoice invoiceWithItems = killBillClient.getInvoice(createdExternalCharge.getInvoiceId(), true);
assertEquals(invoiceWithItems.getBalance().compareTo(chargeAmount), 0);
assertEquals(invoiceWithItems.getItems().size(), 1);
@@ -441,7 +542,7 @@ public class TestInvoice extends TestJaxrsBase {
final Account accountJson = createAccountNoPMBundleAndSubscriptionAndWaitForFirstInvoice();
// Get the invoices
- final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true);
+ final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false);
// 2 invoices but look for the non zero dollar one
assertEquals(invoices.size(), 2);
final UUID invoiceId = invoices.get(1).getInvoiceId();
@@ -455,7 +556,7 @@ public class TestInvoice extends TestJaxrsBase {
externalCharge.setAmount(chargeAmount);
externalCharge.setCurrency(Currency.valueOf(accountJson.getCurrency()));
externalCharge.setInvoiceId(invoiceId);
- final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), false, true, createdBy, reason, comment);
+ final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCToday(), false, true, createdBy, reason, comment);
final Invoice invoiceWithItems = killBillClient.getInvoice(createdExternalCharge.getInvoiceId(), true);
assertEquals(invoiceWithItems.getItems().size(), originalNumberOfItemsForInvoice + 1);
assertNull(invoiceWithItems.getItems().get(originalNumberOfItemsForInvoice).getBundleId());
@@ -471,7 +572,7 @@ public class TestInvoice extends TestJaxrsBase {
final Account accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
// Get the invoices
- final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true);
+ final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false);
// 2 invoices but look for the non zero dollar one
assertEquals(invoices.size(), 2);
final UUID invoiceId = invoices.get(1).getInvoiceId();
@@ -485,7 +586,7 @@ public class TestInvoice extends TestJaxrsBase {
externalCharge.setAmount(chargeAmount);
externalCharge.setCurrency(Currency.valueOf(accountJson.getCurrency()));
externalCharge.setInvoiceId(invoiceId);
- final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), true, true, createdBy, reason, comment);
+ final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCToday(), true, true, createdBy, reason, comment);
final Invoice invoiceWithItems = killBillClient.getInvoice(createdExternalCharge.getInvoiceId(), true);
assertEquals(invoiceWithItems.getItems().size(), originalNumberOfItemsForInvoice + 1);
assertNull(invoiceWithItems.getItems().get(originalNumberOfItemsForInvoice).getBundleId());
@@ -500,7 +601,7 @@ public class TestInvoice extends TestJaxrsBase {
final Account accountJson = createAccountNoPMBundleAndSubscriptionAndWaitForFirstInvoice();
// Get the invoices
- final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true);
+ final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false);
// 2 invoices but look for the non zero dollar one
assertEquals(invoices.size(), 2);
final UUID invoiceId = invoices.get(1).getInvoiceId();
@@ -516,7 +617,7 @@ public class TestInvoice extends TestJaxrsBase {
externalCharge.setCurrency(Currency.valueOf(accountJson.getCurrency()));
externalCharge.setInvoiceId(invoiceId);
externalCharge.setBundleId(bundleId);
- final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), false, true, createdBy, reason, comment);
+ final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCToday(), false, true, createdBy, reason, comment);
final Invoice invoiceWithItems = killBillClient.getInvoice(createdExternalCharge.getInvoiceId(), true);
assertEquals(invoiceWithItems.getItems().size(), originalNumberOfItemsForInvoice + 1);
assertEquals(invoiceWithItems.getItems().get(originalNumberOfItemsForInvoice).getBundleId(), bundleId);
@@ -576,7 +677,38 @@ public class TestInvoice extends TestJaxrsBase {
invoice = killBillClient.getInvoice(creditJson.getInvoiceId());
Assert.assertEquals(invoice.getStatus(), InvoiceStatus.COMMITTED.toString());
+ }
+ @Test(groups = "slow", description = "Can create a migration invoice")
+ public void testInvoiceMigration() throws Exception {
+ final Account accountJson = createAccountNoPMBundleAndSubscriptionAndWaitForFirstInvoice();
+
+ // Get the invoices
+ final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, true);
+ assertEquals(invoices.size(), 2);
+
+ // Migrate an invoice with one external charge
+ final BigDecimal chargeAmount = BigDecimal.TEN;
+ final InvoiceItem externalCharge = new InvoiceItem();
+ externalCharge.setStartDate(new LocalDate());
+ externalCharge.setAccountId(accountJson.getAccountId());
+ externalCharge.setAmount(chargeAmount);
+ externalCharge.setItemType(InvoiceItemType.EXTERNAL_CHARGE.toString());
+ externalCharge.setCurrency(Currency.valueOf(accountJson.getCurrency()));
+
+ final Account accountWithBalance = killBillClient.getAccount(accountJson.getAccountId(), true, true);
+
+ final Invoice migrationInvoice = killBillClient.createMigrationInvoice(accountJson.getAccountId(), null, ImmutableList.<InvoiceItem>of(externalCharge), createdBy, reason, comment);
+ assertEquals(migrationInvoice.getBalance(), BigDecimal.ZERO);
+ assertEquals(migrationInvoice.getItems().size(), 1);
+ assertEquals(migrationInvoice.getItems().get(0).getAmount().compareTo(chargeAmount), 0);
+ assertEquals(migrationInvoice.getItems().get(0).getCurrency(), Currency.valueOf(accountJson.getCurrency()));
+
+
+ final List<Invoice> invoicesWithMigration = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, true);
+ assertEquals(invoicesWithMigration.size(), 3);
+ final Account accountWithBalanceAfterMigration = killBillClient.getAccount(accountJson.getAccountId(), true, true);
+ assertEquals(accountWithBalanceAfterMigration.getAccountBalance().compareTo(accountWithBalance.getAccountBalance()), 0);
}
}
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java
index 09d899a..b76434b 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java
@@ -80,7 +80,11 @@ public class TestPayment extends TestJaxrsBase {
@Test(groups = "slow")
public void testCreateRetrievePayment() throws Exception {
final Account account = createAccountWithDefaultPaymentMethod();
- testCreateRetrievePayment(account, null, UUID.randomUUID().toString(), 1);
+ final String externalPaymentKey = UUID.randomUUID().toString();
+ final UUID paymentId = testCreateRetrievePayment(account, null, externalPaymentKey, 1);
+
+ final Payment payment = killBillClient.getPaymentByExternalKey(externalPaymentKey);
+ assertEquals(payment.getPaymentId(), paymentId);
final PaymentMethod paymentMethodJson = new PaymentMethod(null, UUID.randomUUID().toString(), account.getAccountId(), false, PLUGIN_NAME, new PaymentMethodPluginDetail());
final PaymentMethod nonDefaultPaymentMethod = killBillClient.createPaymentMethod(paymentMethodJson, createdBy, reason, comment);
@@ -156,7 +160,7 @@ public class TestPayment extends TestJaxrsBase {
refundTransaction.setTransactionExternalKey(refundTransactionExternalKey);
refundTransaction.setAmount(purchaseAmount);
refundTransaction.setCurrency(authPayment.getCurrency());
- final Payment refundPayment = killBillClient.refundPayment(refundTransaction, pluginProperties, createdBy, reason, comment);
+ final Payment refundPayment = killBillClient.refundPayment(refundTransaction, null, pluginProperties, createdBy, reason, comment);
verifyPaymentWithPendingRefund(account, paymentMethodId, paymentExternalKey, purchaseTransactionExternalKey, purchaseAmount, refundTransactionExternalKey, refundPayment);
// We cannot complete using just the payment id as JAX-RS doesn't know which transaction to complete
@@ -221,7 +225,7 @@ public class TestPayment extends TestJaxrsBase {
// Void payment using externalKey
final String voidTransactionExternalKey = UUID.randomUUID().toString();
- final Payment voidPayment = killBillClient.voidPayment(null, paymentExternalKey, voidTransactionExternalKey, ImmutableMap.<String, String>of(), createdBy, reason, comment);
+ final Payment voidPayment = killBillClient.voidPayment(null, paymentExternalKey, voidTransactionExternalKey, null, ImmutableMap.<String, String>of(), createdBy, reason, comment);
verifyPaymentTransaction(accountJson, voidPayment.getPaymentId(), paymentExternalKey, voidPayment.getTransactions().get(1),
voidTransactionExternalKey, null, "VOID", "SUCCESS");
}
@@ -244,7 +248,7 @@ public class TestPayment extends TestJaxrsBase {
Assert.assertNull(payment);
}
- private void testCreateRetrievePayment(final Account account, @Nullable final UUID paymentMethodId,
+ private UUID testCreateRetrievePayment(final Account account, @Nullable final UUID paymentMethodId,
final String paymentExternalKey, final int paymentNb) throws Exception {
// Authorization
final String authTransactionExternalKey = UUID.randomUUID().toString();
@@ -290,6 +294,8 @@ public class TestPayment extends TestJaxrsBase {
BigDecimal.TEN, BigDecimal.TEN, new BigDecimal("2"), new BigDecimal("2"), 4, paymentNb);
verifyPaymentTransaction(account, authPayment.getPaymentId(), paymentExternalKey, refundPayment.getTransactions().get(3),
refundTransactionExternalKey, refundTransaction.getAmount(), "REFUND", "SUCCESS");
+
+ return authPayment.getPaymentId();
}
private Payment createVerifyTransaction(final Account account,
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestUsage.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestUsage.java
new file mode 100644
index 0000000..2e9dd6a
--- /dev/null
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestUsage.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2016 Groupon, Inc
+ * Copyright 2016 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.jaxrs;
+
+import java.util.UUID;
+
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.PriceListSet;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.client.model.Account;
+import org.killbill.billing.client.model.Bundle;
+import org.killbill.billing.client.model.RolledUpUsage;
+import org.killbill.billing.client.model.Subscription;
+import org.killbill.billing.client.model.SubscriptionUsageRecord;
+import org.killbill.billing.client.model.UnitUsageRecord;
+import org.killbill.billing.client.model.UsageRecord;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class TestUsage extends TestJaxrsBase {
+
+ @Test(groups = "slow", description = "Can record and retrieve usage data")
+ public void testRecordUsage() throws Exception {
+ final Account accountJson = createAccountWithDefaultPaymentMethod();
+
+ final Subscription base = new Subscription();
+ base.setAccountId(accountJson.getAccountId());
+ base.setProductName("Pistol");
+ base.setProductCategory(ProductCategory.BASE);
+ base.setBillingPeriod(BillingPeriod.MONTHLY);
+ base.setPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
+
+ final Subscription addOn = new Subscription();
+ addOn.setAccountId(accountJson.getAccountId());
+ addOn.setProductName("Bullets");
+ addOn.setProductCategory(ProductCategory.ADD_ON);
+ addOn.setBillingPeriod(BillingPeriod.NO_BILLING_PERIOD);
+ addOn.setPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
+
+ final Bundle bundle = killBillClient.createSubscriptionWithAddOns(ImmutableList.<Subscription>of(base, addOn),
+ null,
+ DEFAULT_WAIT_COMPLETION_TIMEOUT_SEC,
+ createdBy,
+ reason,
+ comment);
+ final UUID addOnSubscriptionId = Iterables.<Subscription>find(bundle.getSubscriptions(),
+ new Predicate<Subscription>() {
+ @Override
+ public boolean apply(final Subscription input) {
+ return ProductCategory.ADD_ON.equals(input.getProductCategory());
+ }
+ }).getSubscriptionId();
+
+ clock.addDays(1);
+
+ final UsageRecord usageRecord1 = new UsageRecord();
+ usageRecord1.setAmount(10L);
+ usageRecord1.setRecordDate(clock.getUTCToday().minusDays(1));
+
+ final UsageRecord usageRecord2 = new UsageRecord();
+ usageRecord2.setAmount(5L);
+ usageRecord2.setRecordDate(clock.getUTCToday());
+
+ final UnitUsageRecord unitUsageRecord = new UnitUsageRecord();
+ unitUsageRecord.setUnitType("bullets");
+ unitUsageRecord.setUsageRecords(ImmutableList.<UsageRecord>of(usageRecord1, usageRecord2));
+
+ final SubscriptionUsageRecord usage = new SubscriptionUsageRecord();
+ usage.setSubscriptionId(addOnSubscriptionId);
+ usage.setUnitUsageRecords(ImmutableList.<UnitUsageRecord>of(unitUsageRecord));
+
+ killBillClient.createSubscriptionUsageRecord(usage, createdBy, reason, comment);
+
+ final RolledUpUsage retrievedUsage1 = killBillClient.getRolledUpUsage(addOnSubscriptionId, unitUsageRecord.getUnitType(), clock.getUTCToday().minusDays(1), clock.getUTCToday());
+ Assert.assertEquals(retrievedUsage1.getSubscriptionId(), usage.getSubscriptionId());
+ Assert.assertEquals(retrievedUsage1.getRolledUpUnits().size(), 1);
+ Assert.assertEquals(retrievedUsage1.getRolledUpUnits().get(0).getUnitType(), unitUsageRecord.getUnitType());
+ // endDate is excluded
+ Assert.assertEquals((long) retrievedUsage1.getRolledUpUnits().get(0).getAmount(), 10);
+
+ final RolledUpUsage retrievedUsage2 = killBillClient.getRolledUpUsage(addOnSubscriptionId, unitUsageRecord.getUnitType(), clock.getUTCToday().minusDays(1), clock.getUTCToday().plusDays(1));
+ Assert.assertEquals(retrievedUsage2.getSubscriptionId(), usage.getSubscriptionId());
+ Assert.assertEquals(retrievedUsage2.getRolledUpUnits().size(), 1);
+ Assert.assertEquals(retrievedUsage2.getRolledUpUnits().get(0).getUnitType(), unitUsageRecord.getUnitType());
+ Assert.assertEquals((long) retrievedUsage2.getRolledUpUnits().get(0).getAmount(), 15);
+
+ final RolledUpUsage retrievedUsage3 = killBillClient.getRolledUpUsage(addOnSubscriptionId, unitUsageRecord.getUnitType(), clock.getUTCToday(), clock.getUTCToday().plusDays(1));
+ Assert.assertEquals(retrievedUsage3.getSubscriptionId(), usage.getSubscriptionId());
+ Assert.assertEquals(retrievedUsage3.getRolledUpUnits().size(), 1);
+ Assert.assertEquals(retrievedUsage3.getRolledUpUnits().get(0).getUnitType(), unitUsageRecord.getUnitType());
+ Assert.assertEquals((long) retrievedUsage3.getRolledUpUnits().get(0).getAmount(), 5);
+
+ final RolledUpUsage retrievedUsage4 = killBillClient.getRolledUpUsage(addOnSubscriptionId, null, clock.getUTCToday(), clock.getUTCToday().plusDays(1));
+ Assert.assertEquals(retrievedUsage4.getSubscriptionId(), usage.getSubscriptionId());
+ Assert.assertEquals(retrievedUsage4.getRolledUpUnits().size(), 1);
+ Assert.assertEquals(retrievedUsage4.getRolledUpUnits().get(0).getUnitType(), "bullets");
+ Assert.assertEquals((long) retrievedUsage4.getRolledUpUnits().get(0).getAmount(), 5);
+ }
+}
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/server/log/ServerTestSuiteNoDB.java b/profiles/killbill/src/test/java/org/killbill/billing/server/log/ServerTestSuiteNoDB.java
index f2328af..f0e5e86 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/server/log/ServerTestSuiteNoDB.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/server/log/ServerTestSuiteNoDB.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -17,7 +17,18 @@
package org.killbill.billing.server.log;
+import org.killbill.billing.GuicyKillbillTestModule;
import org.killbill.billing.GuicyKillbillTestSuiteNoDB;
+import org.testng.annotations.BeforeClass;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
public abstract class ServerTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
+
+ @BeforeClass(groups = "fast")
+ protected void beforeClass() throws Exception {
+ final Injector injector = Guice.createInjector(new TestServerModuleNoDB(configSource));
+ injector.injectMembers(this);
+ }
}
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/server/security/TestKillbillJdbcTenantRealm.java b/profiles/killbill/src/test/java/org/killbill/billing/server/security/TestKillbillJdbcTenantRealm.java
index a82fe1e..b5b51ce 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/server/security/TestKillbillJdbcTenantRealm.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/server/security/TestKillbillJdbcTenantRealm.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -30,7 +30,9 @@ import org.killbill.billing.jaxrs.TestJaxrsBase;
import org.killbill.billing.tenant.api.DefaultTenant;
import org.killbill.billing.tenant.dao.DefaultTenantDao;
import org.killbill.billing.tenant.dao.TenantModelDao;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.dao.DefaultNonEntityDao;
+import org.mockito.Mockito;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -49,7 +51,7 @@ public class TestKillbillJdbcTenantRealm extends TestJaxrsBase {
super.beforeMethod();
// Create the tenant
- final DefaultTenantDao tenantDao = new DefaultTenantDao(dbi, clock, cacheControllerDispatcher, new DefaultNonEntityDao(dbi), securityConfig);
+ final DefaultTenantDao tenantDao = new DefaultTenantDao(dbi, clock, cacheControllerDispatcher, new DefaultNonEntityDao(dbi), Mockito.mock(InternalCallContextFactory.class), securityConfig);
tenant = new DefaultTenant(UUID.randomUUID(), null, null, UUID.randomUUID().toString(),
UUID.randomUUID().toString(), UUID.randomUUID().toString());
tenantDao.create(new TenantModelDao(tenant), internalCallContext);
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/alignment/PlanAligner.java b/subscription/src/main/java/org/killbill/billing/subscription/alignment/PlanAligner.java
index fe4e369..f1eef82 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/alignment/PlanAligner.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/alignment/PlanAligner.java
@@ -151,9 +151,7 @@ public class PlanAligner extends BaseAligner {
switch (lastPlanTransition.getTransitionType()) {
// If we never had any Plan change, borrow the logic for createPlan alignment
- case MIGRATE_ENTITLEMENT:
case CREATE:
- case RE_CREATE:
case TRANSFER:
final List<TimedPhase> timedPhases = getTimedPhaseOnCreate(subscription.getAlignStartDate(),
subscription.getBundleStartDate(),
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java b/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java
index b803399..c6c1855 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java
@@ -31,7 +31,6 @@ import org.killbill.billing.catalog.api.PhaseType;
import org.killbill.billing.catalog.api.Plan;
import org.killbill.billing.catalog.api.PlanChangeResult;
import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
-import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
import org.killbill.billing.subscription.api.user.SubscriptionBuilder;
@@ -47,11 +46,7 @@ public interface SubscriptionBaseApiService {
CallContext context)
throws SubscriptionBaseApiException;
- public DefaultSubscriptionBase createPlans(Iterable<SubscriptionSpecifier> subscriptions, CallContext context)
- throws SubscriptionBaseApiException;
-
- @Deprecated
- public boolean recreatePlan(DefaultSubscriptionBase subscription, PlanPhaseSpecifier spec, List<PlanPhasePriceOverride> overrides, DateTime requestedDateWithMs, CallContext context)
+ public List<DefaultSubscriptionBase> createPlans(Iterable<SubscriptionSpecifier> subscriptions, CallContext context)
throws SubscriptionBaseApiException;
public boolean cancel(DefaultSubscriptionBase subscription, CallContext context)
@@ -96,10 +91,10 @@ public interface SubscriptionBaseApiService {
//
// Lower level APIs for dryRun functionality
//
- public List<SubscriptionBaseEvent> getEventsOnCreation(UUID bundleId, UUID subscriptionId, DateTime alignStartDate, DateTime bundleStartDate, long activeVersion,
+ public List<SubscriptionBaseEvent> getEventsOnCreation(UUID bundleId, UUID subscriptionId, DateTime alignStartDate, DateTime bundleStartDate,
Plan plan, PhaseType initialPhase,
String realPriceList, DateTime effectiveDate, DateTime processedDate,
- boolean reCreate, InternalTenantContext context)
+ InternalTenantContext context)
throws CatalogApiException, SubscriptionBaseApiException;
public List<SubscriptionBaseEvent> getEventsOnChangePlan(DefaultSubscriptionBase subscription, Plan newPlan,
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
index 4b17ee0..5ef474f 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
@@ -122,7 +122,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
}
@Override
- public SubscriptionBase createSubscription(final UUID bundleId, final PlanPhaseSpecifier spec, final List<PlanPhasePriceOverride> overrides, final DateTime requestedDateWithMs, final InternalCallContext context) throws SubscriptionBaseApiException {
+ public SubscriptionBase createSubscription(final UUID bundleId, final PlanPhaseSpecifier spec, final List<PlanPhasePriceOverride> overrides, final DateTime requestedDateWithMs, final boolean isMigrated, final InternalCallContext context) throws SubscriptionBaseApiException {
try {
final String realPriceList = (spec.getPriceListName() == null) ? PriceListSet.DEFAULT_PRICELIST_NAME : spec.getPriceListName();
final DateTime now = clock.getUTCNow();
@@ -156,7 +156,8 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
.setBundleId(bundleId)
.setCategory(plan.getProduct().getCategory())
.setBundleStartDate(bundleStartDate)
- .setAlignStartDate(effectiveDate),
+ .setAlignStartDate(effectiveDate)
+ .setMigrated(isMigrated),
plan, spec.getPhaseType(), realPriceList, effectiveDate, now, callContext);
} catch (final CatalogApiException e) {
throw new SubscriptionBaseApiException(e);
@@ -164,7 +165,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
}
@Override
- public SubscriptionBase createBaseSubscriptionWithAddOns(final UUID bundleId, final Iterable<EntitlementSpecifier> entitlements, final DateTime requestedDateWithMs, final InternalCallContext context) throws SubscriptionBaseApiException {
+ public List<SubscriptionBase> createBaseSubscriptionWithAddOns(final UUID bundleId, final Iterable<EntitlementSpecifier> entitlements, final DateTime requestedDateWithMs, final boolean isMigrated, final InternalCallContext context) throws SubscriptionBaseApiException {
final DateTime now = clock.getUTCNow();
final DateTime effectiveDate = (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : now;
@@ -204,12 +205,19 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
.setBundleId(bundleId)
.setCategory(plan.getProduct().getCategory())
.setBundleStartDate(effectiveDate)
- .setAlignStartDate(effectiveDate));
+ .setAlignStartDate(effectiveDate)
+ .setMigrated(isMigrated));
subscriptions.add(subscription);
}
- return apiService.createPlans(subscriptions, callContext);
+ final List<DefaultSubscriptionBase> result = apiService.createPlans(subscriptions, callContext);
+ return ImmutableList.copyOf(Iterables.transform(result, new Function<DefaultSubscriptionBase, SubscriptionBase>() {
+ @Override
+ public SubscriptionBase apply(final DefaultSubscriptionBase input) {
+ return (SubscriptionBase) input;
+ }
+ }));
} catch (final CatalogApiException e) {
throw new SubscriptionBaseApiException(e);
}
@@ -512,11 +520,11 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
case START_BILLING:
final DefaultSubscriptionBase baseSubscription = (DefaultSubscriptionBase) dao.getBaseSubscription(bundleId, context);
- final DateTime startEffectiveDate = dryRunArguments.getEffectiveDate() != null ? dryRunArguments.getEffectiveDate() : utcNow;
+ final DateTime startEffectiveDate = dryRunArguments.getEffectiveDate() != null ? context.toUTCDateTime(dryRunArguments.getEffectiveDate()) : utcNow;
final DateTime bundleStartDate = getBundleStartDateWithSanity(bundleId, baseSubscription, plan, startEffectiveDate, context);
final UUID subscriptionId = UUIDs.randomUUID();
- dryRunEvents = apiService.getEventsOnCreation(bundleId, subscriptionId, startEffectiveDate, bundleStartDate, 1L, plan, inputSpec.getPhaseType(), realPriceList,
- startEffectiveDate, utcNow, false, context);
+ dryRunEvents = apiService.getEventsOnCreation(bundleId, subscriptionId, startEffectiveDate, bundleStartDate, plan, inputSpec.getPhaseType(), realPriceList,
+ startEffectiveDate, utcNow, context);
final SubscriptionBuilder builder = new SubscriptionBuilder()
.setId(subscriptionId)
.setBundleId(bundleId)
@@ -530,7 +538,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
case CHANGE:
final DefaultSubscriptionBase subscriptionForChange = (DefaultSubscriptionBase) dao.getSubscriptionFromId(dryRunArguments.getSubscriptionId(), context);
- DateTime changeEffectiveDate = dryRunArguments.getEffectiveDate();
+ DateTime changeEffectiveDate = dryRunArguments.getEffectiveDate() != null ? context.toUTCDateTime(dryRunArguments.getEffectiveDate()) : null;
if (changeEffectiveDate == null) {
BillingActionPolicy policy = dryRunArguments.getBillingActionPolicy();
if (policy == null) {
@@ -547,7 +555,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
case STOP_BILLING:
final DefaultSubscriptionBase subscriptionForCancellation = (DefaultSubscriptionBase) dao.getSubscriptionFromId(dryRunArguments.getSubscriptionId(), context);
- DateTime cancelEffectiveDate = dryRunArguments.getEffectiveDate();
+ DateTime cancelEffectiveDate = dryRunArguments.getEffectiveDate() != null ? context.toUTCDateTime(dryRunArguments.getEffectiveDate()) : null;
if (dryRunArguments.getEffectiveDate() == null) {
BillingActionPolicy policy = dryRunArguments.getBillingActionPolicy();
if (policy == null) {
@@ -619,7 +627,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
switch (plan.getProduct().getCategory()) {
case BASE:
if (baseSubscription != null &&
- baseSubscription.getState() == EntitlementState.ACTIVE) {
+ (baseSubscription.getState() == EntitlementState.ACTIVE || baseSubscription.getState() == EntitlementState.PENDING)) {
throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_BP_EXISTS, bundleId);
}
return effectiveDate;
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimeline.java b/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimeline.java
index fc33b62..436393f 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimeline.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimeline.java
@@ -45,12 +45,10 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
private final UUID id;
private final List<ExistingEvent> existingEvents;
- private final long activeVersion;
public DefaultSubscriptionBaseTimeline(final DefaultSubscriptionBase input, final Catalog catalog) throws CatalogApiException {
this.id = input.getId();
- this.existingEvents = toExistingEvents(catalog, input.getActiveVersion(), input.getCategory(), input.getEvents());
- this.activeVersion = input.getActiveVersion();
+ this.existingEvents = toExistingEvents(catalog, input.getCategory(), input.getEvents());
}
private BillingPeriod getBillingPeriod(final Catalog catalog, @Nullable final String phaseName, final DateTime effectiveDate, DateTime startDate) throws CatalogApiException {
@@ -61,7 +59,7 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
return phase.getRecurring() != null ? phase.getRecurring().getBillingPeriod() : BillingPeriod.NO_BILLING_PERIOD;
}
- private List<ExistingEvent> toExistingEvents(final Catalog catalog, final long activeVersion, final ProductCategory category, final List<SubscriptionBaseEvent> events)
+ private List<ExistingEvent> toExistingEvents(final Catalog catalog, final ProductCategory category, final List<SubscriptionBaseEvent> events)
throws CatalogApiException {
final List<ExistingEvent> result = new LinkedList<SubscriptionBaseTimeline.ExistingEvent>();
@@ -76,11 +74,7 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
for (final SubscriptionBaseEvent cur : events) {
- // First active event is used to figure out which catalog version to use.
- //startDate = (startDate == null && cur.getActiveVersion() == activeVersion) ? cur.getEffectiveDate() : startDate;
-
- // STEPH that needs to be reviewed if we support multi version events
- if (cur.getActiveVersion() != activeVersion || !cur.isActive()) {
+ if (!cur.isActive()) {
continue;
}
startDate = (startDate == null) ? cur.getEffectiveDate() : startDate;
@@ -130,11 +124,6 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
}
@Override
- public DateTime getRequestedDate() {
- return cur.getEffectiveDate();
- }
-
- @Override
public PlanPhaseSpecifier getPlanPhaseSpecifier() {
return spec;
}
@@ -191,11 +180,6 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
return existingEvents;
}
- @Override
- public long getActiveVersion() {
- return activeVersion;
- }
-
private void sortExistingEvent(final List<ExistingEvent> events) {
if (events != null) {
Collections.sort(events, new Comparator<ExistingEvent>() {
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
index 49c3ca6..e7ec71a 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
@@ -37,8 +37,6 @@ import org.killbill.clock.Clock;
import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
import org.killbill.billing.subscription.api.SubscriptionApiBase;
import org.killbill.billing.subscription.api.SubscriptionBaseApiService;
-import org.killbill.billing.subscription.api.migration.AccountMigrationData.BundleMigrationData;
-import org.killbill.billing.subscription.api.migration.AccountMigrationData.SubscriptionMigrationData;
import org.killbill.billing.subscription.api.svcs.DefaultSubscriptionInternalApi;
import org.killbill.billing.subscription.api.timeline.BundleBaseTimeline;
import org.killbill.billing.subscription.api.timeline.SubscriptionBaseRepairException;
@@ -101,14 +99,11 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
.setEventPlan(existingEvent.getPlanName())
.setEventPlanPhase(currentPhase.getName())
.setEventPriceList(spec.getPriceListName())
- .setActiveVersion(subscription.getActiveVersion())
.setEffectiveDate(effectiveDate)
.setFromDisk(true);
switch (existingEvent.getSubscriptionTransitionType()) {
case TRANSFER:
- case MIGRATE_ENTITLEMENT:
- case RE_CREATE:
case CREATE:
newEvent = new ApiEventTransfer(apiBuilder);
break;
@@ -120,15 +115,9 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
case PHASE:
newEvent = firstEvent ? new ApiEventTransfer(apiBuilder) :
- PhaseEventData.createNextPhaseEvent(subscription.getId(), subscription.getActiveVersion(), currentPhase.getName(), effectiveDate);
+ PhaseEventData.createNextPhaseEvent(subscription.getId(), currentPhase.getName(), effectiveDate);
break;
- // Ignore these events except if it's the first event for the new subscription
- case MIGRATE_BILLING:
- if (firstEvent) {
- newEvent = new ApiEventTransfer(apiBuilder);
- }
- break;
case CANCEL:
break;
@@ -213,7 +202,7 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
final DefaultSubscriptionBaseBundle subscriptionBundleData = new DefaultSubscriptionBaseBundle(bundleKey, destAccountId, effectiveTransferDate,
bundle.getOriginalCreatedDate(), clock.getUTCNow(), clock.getUTCNow());
- final List<SubscriptionMigrationData> subscriptionMigrationDataList = new LinkedList<SubscriptionMigrationData>();
+ final List<SubscriptionTransferData> subscriptionTransferDataList = new LinkedList<SubscriptionTransferData>();
final List<TransferCancelData> transferCancelDataList = new LinkedList<TransferCancelData>();
@@ -238,11 +227,10 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
final SubscriptionBaseEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
.setSubscriptionId(cur.getId())
- .setActiveVersion(cur.getActiveVersion())
.setEffectiveDate(effectiveCancelDate)
.setFromDisk(true));
- TransferCancelData cancelData = new TransferCancelData(oldSubscription, cancelEvent);
+ final TransferCancelData cancelData = new TransferCancelData(oldSubscription, cancelEvent);
transferCancelDataList.add(cancelData);
}
@@ -266,15 +254,15 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
ImmutableList.<SubscriptionBaseEvent>of(), fromInternalCallContext);
final List<SubscriptionBaseEvent> events = toEvents(existingEvents, defaultSubscriptionBase, effectiveTransferDate, fromInternalCallContext);
- final SubscriptionMigrationData curData = new SubscriptionMigrationData(defaultSubscriptionBase, events, null);
- subscriptionMigrationDataList.add(curData);
+ final SubscriptionTransferData curData = new SubscriptionTransferData(defaultSubscriptionBase, events, null);
+ subscriptionTransferDataList.add(curData);
}
- BundleMigrationData bundleMigrationData = new BundleMigrationData(subscriptionBundleData, subscriptionMigrationDataList);
+ BundleTransferData bundleTransferData = new BundleTransferData(subscriptionBundleData, subscriptionTransferDataList);
// Atomically cancelWithRequestedDate all subscription on old account and create new bundle, subscriptions, events for new account
- dao.transfer(sourceAccountId, destAccountId, bundleMigrationData, transferCancelDataList, fromInternalCallContext, toInternalCallContext);
+ dao.transfer(sourceAccountId, destAccountId, bundleTransferData, transferCancelDataList, fromInternalCallContext, toInternalCallContext);
- return bundleMigrationData.getData();
+ return bundleTransferData.getData();
} catch (SubscriptionBaseRepairException e) {
throw new SubscriptionBaseTransferApiException(e);
} catch (CatalogApiException e) {
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java
index 6f04266..9594dc5 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java
@@ -73,12 +73,12 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
private final DateTime alignStartDate;
private final DateTime bundleStartDate;
private final ProductCategory category;
+ private final boolean migrated;
//
// Those can be modified through non User APIs, and a new SubscriptionBase
// object would be created
//
- private final long activeVersion;
private final DateTime chargedThroughDate;
//
@@ -108,8 +108,8 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
this.alignStartDate = builder.getAlignStartDate();
this.bundleStartDate = builder.getBundleStartDate();
this.category = builder.getCategory();
- this.activeVersion = builder.getActiveVersion();
this.chargedThroughDate = builder.getChargedThroughDate();
+ this.migrated = builder.isMigrated();
}
// Used for API to make sure we have a clock and an apiService set before we return the object
@@ -121,8 +121,8 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
this.alignStartDate = internalSubscription.getAlignStartDate();
this.bundleStartDate = internalSubscription.getBundleStartDate();
this.category = internalSubscription.getCategory();
- this.activeVersion = internalSubscription.getActiveVersion();
this.chargedThroughDate = internalSubscription.getChargedThroughDate();
+ this.migrated = internalSubscription.isMigrated();
this.transitions = new LinkedList<SubscriptionBaseTransition>(internalSubscription.getAllTransitions());
this.events = internalSubscription.getEvents();
}
@@ -139,8 +139,19 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
@Override
public EntitlementState getState() {
- return (getPreviousTransition() == null) ? null
- : getPreviousTransition().getNextState();
+
+ final SubscriptionBaseTransition previousTransition = getPreviousTransition();
+ if (previousTransition != null) {
+ return previousTransition.getNextState();
+ }
+
+ final SubscriptionBaseTransition pendingTransition = getPendingTransition();
+ if (pendingTransition != null &&
+ (pendingTransition.getTransitionType().equals(SubscriptionBaseTransitionType.CREATE) ||
+ pendingTransition.getTransitionType().equals(SubscriptionBaseTransitionType.TRANSFER))) {
+ return EntitlementState.PENDING;
+ }
+ throw new IllegalStateException("Should return a valid EntitlementState");
}
@Override
@@ -148,15 +159,16 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
if (transitions == null) {
return null;
}
- final SubscriptionBaseTransitionData initialTransition = (SubscriptionBaseTransitionData) transitions.get(0);
- switch (initialTransition.getApiEventType()) {
- case MIGRATE_BILLING:
- case MIGRATE_ENTITLEMENT:
- return EntitlementSourceType.MIGRATED;
- case TRANSFER:
- return EntitlementSourceType.TRANSFERRED;
- default:
- return EntitlementSourceType.NATIVE;
+ if (isMigrated()) {
+ return EntitlementSourceType.MIGRATED;
+ } else {
+ final SubscriptionBaseTransitionData initialTransition = (SubscriptionBaseTransitionData) transitions.get(0);
+ switch (initialTransition.getApiEventType()) {
+ case TRANSFER:
+ return EntitlementSourceType.TRANSFERRED;
+ default:
+ return EntitlementSourceType.NATIVE;
+ }
}
}
@@ -206,11 +218,6 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
return null;
}
- public boolean recreate(final PlanPhaseSpecifier spec, final List<PlanPhasePriceOverride> overrides, final DateTime requestedDate,
- final CallContext context) throws SubscriptionBaseApiException {
- return apiService.recreatePlan(this, spec, overrides, requestedDate, context);
- }
-
@Override
public boolean cancel(final CallContext context) throws SubscriptionBaseApiException {
return apiService.cancel(this, context);
@@ -353,6 +360,11 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
}
@Override
+ public boolean isMigrated() {
+ return migrated;
+ }
+
+ @Override
public List<SubscriptionBaseTransition> getAllTransitions() {
if (transitions == null) {
return Collections.emptyList();
@@ -432,10 +444,6 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
return it.hasNext() ? ((SubscriptionBaseTransitionData) it.next()).getTotalOrdering() : -1L;
}
- public long getActiveVersion() {
- return activeVersion;
- }
-
public List<SubscriptionBaseTransition> getBillingTransitions() {
if (transitions == null) {
@@ -445,14 +453,13 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
final SubscriptionBaseTransitionDataIterator it = new SubscriptionBaseTransitionDataIterator(
clock, transitions, Order.ASC_FROM_PAST, Kind.BILLING,
Visibility.ALL, TimeLimit.ALL);
- // Remove anything prior to first CREATE or MIGRATE_BILLING
+ // Remove anything prior to first CREATE
boolean foundInitialEvent = false;
while (it.hasNext()) {
final SubscriptionBaseTransitionData curTransition = (SubscriptionBaseTransitionData) it.next();
if (!foundInitialEvent) {
foundInitialEvent = curTransition.getEventType() == EventType.API_USER &&
(curTransition.getApiEventType() == ApiEventType.CREATE ||
- curTransition.getApiEventType() == ApiEventType.MIGRATE_BILLING ||
curTransition.getApiEventType() == ApiEventType.TRANSFER);
}
if (foundInitialEvent) {
@@ -477,10 +484,8 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
while (it.hasNext()) {
final SubscriptionBaseTransitionData cur = (SubscriptionBaseTransitionData) it.next();
if (cur.getTransitionType() == SubscriptionBaseTransitionType.CREATE
- || cur.getTransitionType() == SubscriptionBaseTransitionType.RE_CREATE
|| cur.getTransitionType() == SubscriptionBaseTransitionType.TRANSFER
- || cur.getTransitionType() == SubscriptionBaseTransitionType.CHANGE
- || cur.getTransitionType() == SubscriptionBaseTransitionType.MIGRATE_ENTITLEMENT) {
+ || cur.getTransitionType() == SubscriptionBaseTransitionType.CHANGE) {
return cur;
}
}
@@ -530,9 +535,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
if (cur.getTransitionType() == SubscriptionBaseTransitionType.PHASE
|| cur.getTransitionType() == SubscriptionBaseTransitionType.TRANSFER
|| cur.getTransitionType() == SubscriptionBaseTransitionType.CREATE
- || cur.getTransitionType() == SubscriptionBaseTransitionType.RE_CREATE
- || cur.getTransitionType() == SubscriptionBaseTransitionType.CHANGE
- || cur.getTransitionType() == SubscriptionBaseTransitionType.MIGRATE_ENTITLEMENT) {
+ || cur.getTransitionType() == SubscriptionBaseTransitionType.CHANGE) {
return cur.getEffectiveTransitionTime();
}
}
@@ -555,7 +558,6 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
EntitlementState nextState = null;
String nextPlanName = null;
String nextPhaseName = null;
- String nextPriceListName = null;
UUID prevEventId = null;
DateTime prevCreatedDate = null;
@@ -568,7 +570,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
for (final SubscriptionBaseEvent cur : inputEvents) {
- if (!cur.isActive() || cur.getActiveVersion() < activeVersion) {
+ if (!cur.isActive()) {
continue;
}
@@ -593,10 +595,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
switch (apiEventType) {
case TRANSFER:
- case MIGRATE_BILLING:
- case MIGRATE_ENTITLEMENT:
case CREATE:
- case RE_CREATE:
prevEventId = null;
prevCreatedDate = null;
previousState = null;
@@ -606,12 +605,10 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
nextState = EntitlementState.ACTIVE;
nextPlanName = userEV.getEventPlan();
nextPhaseName = userEV.getEventPlanPhase();
- nextPriceListName = userEV.getPriceList();
break;
case CHANGE:
nextPlanName = userEV.getEventPlan();
nextPhaseName = userEV.getEventPlanPhase();
- nextPriceListName = userEV.getPriceList();
break;
case CANCEL:
nextState = EntitlementState.CANCELLED;
@@ -636,10 +633,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
nextPlan = (nextPlanName != null) ? catalog.findPlan(nextPlanName, cur.getEffectiveDate(), getAlignStartDate()) : null;
nextPhase = (nextPhaseName != null) ? catalog.findPhase(nextPhaseName, cur.getEffectiveDate(), getAlignStartDate()) : null;
-
- // See issue https://github.com/killbill/killbill/issues/464
- final DateTime catalogEffectiveDateForPriceList = transitions.isEmpty() ? cur.getEffectiveDate() : transitions.get(0).getEffectiveTransitionTime();
- nextPriceList = (nextPriceListName != null) ? catalog.findPriceList(nextPriceListName, catalogEffectiveDateForPriceList) : null;
+ nextPriceList = (nextPlan != null) ? nextPlan.getPriceList() : null;
final SubscriptionBaseTransitionData transition = new SubscriptionBaseTransitionData(
cur.getId(), id, bundleId, cur.getType(), apiEventType,
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
index b12935f..bab81c5 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
@@ -41,13 +41,11 @@ import org.killbill.billing.catalog.api.CatalogService;
import org.killbill.billing.catalog.api.PhaseType;
import org.killbill.billing.catalog.api.Plan;
import org.killbill.billing.catalog.api.PlanChangeResult;
-import org.killbill.billing.catalog.api.PlanPhase;
import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
import org.killbill.billing.catalog.api.PlanPhasePriceOverridesWithCallContext;
import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
import org.killbill.billing.catalog.api.PlanSpecifier;
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.ProductCategory;
import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
@@ -66,9 +64,7 @@ import org.killbill.billing.subscription.events.user.ApiEventBuilder;
import org.killbill.billing.subscription.events.user.ApiEventCancel;
import org.killbill.billing.subscription.events.user.ApiEventChange;
import org.killbill.billing.subscription.events.user.ApiEventCreate;
-import org.killbill.billing.subscription.events.user.ApiEventReCreate;
import org.killbill.billing.subscription.events.user.ApiEventUncancel;
-import org.killbill.billing.subscription.exceptions.SubscriptionBaseError;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.callcontext.TenantContext;
@@ -108,12 +104,21 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
final CallContext context) throws SubscriptionBaseApiException {
final DefaultSubscriptionBase subscription = new DefaultSubscriptionBase(builder, this, clock);
- createFromSubscription(subscription, plan, initialPhase, realPriceList, effectiveDate, processedDate, false, context);
- return subscription;
+ final InternalCallContext internalCallContext = createCallContextFromBundleId(subscription.getBundleId(), context);
+
+ try {
+ final List<SubscriptionBaseEvent> events = getEventsOnCreation(subscription.getBundleId(), subscription.getId(), subscription.getAlignStartDate(), subscription.getBundleStartDate(),
+ plan, initialPhase, realPriceList, effectiveDate, processedDate, internalCallContext);
+ dao.createSubscription(subscription, events, internalCallContext);
+ subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId(), internalCallContext), catalogService.getFullCatalog(internalCallContext));
+ return subscription;
+ } catch (final CatalogApiException e) {
+ throw new SubscriptionBaseApiException(e);
+ }
}
@Override
- public DefaultSubscriptionBase createPlans(final Iterable<SubscriptionSpecifier> subscriptions, final CallContext context) throws SubscriptionBaseApiException {
+ public List<DefaultSubscriptionBase> createPlans(final Iterable<SubscriptionSpecifier> subscriptions, final CallContext context) throws SubscriptionBaseApiException {
Map<UUID, List<SubscriptionBaseEvent>> eventsMap = new HashMap<UUID, List<SubscriptionBaseEvent>>();
List<DefaultSubscriptionBase> subscriptionBaseList = new ArrayList<DefaultSubscriptionBase>();
@@ -123,10 +128,9 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
final DefaultSubscriptionBase subscriptionBase = new DefaultSubscriptionBase(subscription.getBuilder(), this, clock);
final InternalCallContext internalCallContext = createCallContextFromBundleId(subscriptionBase.getBundleId(), context);
final List<SubscriptionBaseEvent> events = getEventsOnCreation(subscriptionBase.getBundleId(), subscriptionBase.getId(), subscriptionBase.getAlignStartDate(),
- subscriptionBase.getBundleStartDate(), subscriptionBase.getActiveVersion(), subscription.getPlan(),
+ subscriptionBase.getBundleStartDate(), subscription.getPlan(),
subscription.getInitialPhase(), subscription.getRealPriceList(),
- subscription.getEffectiveDate(), subscription.getProcessedDate(), false, internalCallContext);
-
+ subscription.getEffectiveDate(), subscription.getProcessedDate(), internalCallContext);
eventsMap.put(subscriptionBase.getId(), events);
subscriptionBaseList.add(subscriptionBase);
@@ -142,11 +146,19 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
try {
baseSubscription.rebuildTransitions(dao.getEventsForSubscription(baseSubscription.getId(), internalCallContext),
catalogService.getFullCatalog(internalCallContext));
+
+ for (final DefaultSubscriptionBase input : subscriptionBaseList) {
+ if (input.getId().equals(baseSubscription.getId())) {
+ continue;
+ }
+
+ input.rebuildTransitions(dao.getEventsForSubscription(input.getId(), internalCallContext),
+ catalogService.getFullCatalog(internalCallContext));
+ }
} catch (CatalogApiException e) {
throw new SubscriptionBaseApiException(e);
}
-
- return baseSubscription;
+ return subscriptionBaseList;
}
private DefaultSubscriptionBase findBaseSubscription(final List<DefaultSubscriptionBase> subscriptionBaseList) {
@@ -158,60 +170,10 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
}).orNull();
}
- @Deprecated
- @Override
- public boolean recreatePlan(final DefaultSubscriptionBase subscription, final PlanPhaseSpecifier spec, final List<PlanPhasePriceOverride> overrides, final DateTime requestedDateWithMs, final CallContext context)
- throws SubscriptionBaseApiException {
- final EntitlementState currentState = subscription.getState();
- if (currentState != null && currentState != EntitlementState.CANCELLED) {
- throw new SubscriptionBaseApiException(ErrorCode.SUB_RECREATE_BAD_STATE, subscription.getId(), currentState);
- }
-
- final DateTime now = clock.getUTCNow();
- final DateTime effectiveDate = (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : now;
- validateEffectiveDate(subscription, effectiveDate);
-
- try {
- final String realPriceList = (spec.getPriceListName() == null) ? PriceListSet.DEFAULT_PRICELIST_NAME : spec.getPriceListName();
- final InternalTenantContext internalCallContext = createTenantContextFromBundleId(subscription.getBundleId(), context);
- final PlanPhasePriceOverridesWithCallContext overridesWithContext = new DefaultPlanPhasePriceOverridesWithCallContext(overrides, context);
- final Plan plan = catalogService.getFullCatalog(internalCallContext).createOrFindPlan(spec.getProductName(), spec.getBillingPeriod(), realPriceList, overridesWithContext, effectiveDate);
- final PlanPhase phase = plan.getAllPhases()[0];
- if (phase == null) {
- throw new SubscriptionBaseError(String.format("No initial PlanPhase for Product %s, term %s and set %s does not exist in the catalog",
- spec.getProductName(), spec.getBillingPeriod().toString(), realPriceList));
- }
-
- createFromSubscription(subscription, plan, spec.getPhaseType(), realPriceList, effectiveDate, now, true, context);
- return true;
- } catch (final CatalogApiException e) {
- throw new SubscriptionBaseApiException(e);
- }
- }
-
- private void createFromSubscription(final DefaultSubscriptionBase subscription, final Plan plan, final PhaseType initialPhase,
- final String realPriceList, final DateTime effectiveDate, final DateTime processedDate,
- final boolean reCreate, final CallContext context) throws SubscriptionBaseApiException {
- final InternalCallContext internalCallContext = createCallContextFromBundleId(subscription.getBundleId(), context);
-
- try {
- final List<SubscriptionBaseEvent> events = getEventsOnCreation(subscription.getBundleId(), subscription.getId(), subscription.getAlignStartDate(), subscription.getBundleStartDate(), subscription.getActiveVersion(),
- plan, initialPhase, realPriceList, effectiveDate, processedDate, reCreate, internalCallContext);
- if (reCreate) {
- dao.recreateSubscription(subscription, events, internalCallContext);
- } else {
- dao.createSubscription(subscription, events, internalCallContext);
- }
- subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId(), internalCallContext), catalogService.getFullCatalog(internalCallContext));
- } catch (final CatalogApiException e) {
- throw new SubscriptionBaseApiException(e);
- }
- }
-
@Override
public boolean cancel(final DefaultSubscriptionBase subscription, final CallContext context) throws SubscriptionBaseApiException {
final EntitlementState currentState = subscription.getState();
- if (currentState != null && currentState != EntitlementState.ACTIVE) {
+ if (currentState == EntitlementState.CANCELLED) {
throw new SubscriptionBaseApiException(ErrorCode.SUB_CANCEL_BAD_STATE, subscription.getId(), currentState);
}
final DateTime now = clock.getUTCNow();
@@ -237,7 +199,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
@Override
public boolean cancelWithRequestedDate(final DefaultSubscriptionBase subscription, final DateTime requestedDateWithMs, final CallContext context) throws SubscriptionBaseApiException {
final EntitlementState currentState = subscription.getState();
- if (currentState != null && currentState != EntitlementState.ACTIVE) {
+ if (currentState == EntitlementState.CANCELLED) {
throw new SubscriptionBaseApiException(ErrorCode.SUB_CANCEL_BAD_STATE, subscription.getId(), currentState);
}
final DateTime now = clock.getUTCNow();
@@ -250,7 +212,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
@Override
public boolean cancelWithPolicy(final DefaultSubscriptionBase subscription, final BillingActionPolicy policy, final CallContext context) throws SubscriptionBaseApiException {
final EntitlementState currentState = subscription.getState();
- if (currentState != null && currentState != EntitlementState.ACTIVE) {
+ if (currentState == EntitlementState.CANCELLED) {
throw new SubscriptionBaseApiException(ErrorCode.SUB_CANCEL_BAD_STATE, subscription.getId(), currentState);
}
@@ -312,7 +274,6 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
final DateTime now = clock.getUTCNow();
final SubscriptionBaseEvent uncancelEvent = new ApiEventUncancel(new ApiEventBuilder()
.setSubscriptionId(subscription.getId())
- .setActiveVersion(subscription.getActiveVersion())
.setEffectiveDate(now)
.setFromDisk(true));
@@ -322,7 +283,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
final InternalCallContext internalCallContext = createCallContextFromBundleId(subscription.getBundleId(), context);
final TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, now, internalCallContext);
final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
- PhaseEventData.createNextPhaseEvent(subscription.getId(), subscription.getActiveVersion(), nextTimedPhase.getPhase().getName(), nextTimedPhase.getStartPhase()) :
+ PhaseEventData.createNextPhaseEvent(subscription.getId(), nextTimedPhase.getPhase().getName(), nextTimedPhase.getStartPhase()) :
null;
if (nextPhaseEvent != null) {
uncancelEvents.add(nextPhaseEvent);
@@ -468,10 +429,10 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
}
@Override
- public List<SubscriptionBaseEvent> getEventsOnCreation(final UUID bundleId, final UUID subscriptionId, final DateTime alignStartDate, final DateTime bundleStartDate, final long activeVersion,
+ public List<SubscriptionBaseEvent> getEventsOnCreation(final UUID bundleId, final UUID subscriptionId, final DateTime alignStartDate, final DateTime bundleStartDate,
final Plan plan, final PhaseType initialPhase,
final String realPriceList, final DateTime effectiveDate, final DateTime processedDate,
- final boolean reCreate, final InternalTenantContext internalTenantContext) throws CatalogApiException, SubscriptionBaseApiException {
+ final InternalTenantContext internalTenantContext) throws CatalogApiException, SubscriptionBaseApiException {
final TimedPhase[] curAndNextPhases = planAligner.getCurrentAndNextTimedPhaseOnCreate(alignStartDate, bundleStartDate, plan, initialPhase,
realPriceList, effectiveDate, internalTenantContext);
@@ -480,14 +441,13 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
.setEventPlan(plan.getName())
.setEventPlanPhase(curAndNextPhases[0].getPhase().getName())
.setEventPriceList(realPriceList)
- .setActiveVersion(activeVersion)
.setEffectiveDate(effectiveDate)
.setFromDisk(true);
- final ApiEvent creationEvent = (reCreate) ? new ApiEventReCreate(createBuilder) : new ApiEventCreate(createBuilder);
+ final ApiEvent creationEvent = new ApiEventCreate(createBuilder);
final TimedPhase nextTimedPhase = curAndNextPhases[1];
final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
- PhaseEventData.createNextPhaseEvent(subscriptionId, activeVersion, nextTimedPhase.getPhase().getName(), nextTimedPhase.getStartPhase()) :
+ PhaseEventData.createNextPhaseEvent(subscriptionId, nextTimedPhase.getPhase().getName(), nextTimedPhase.getStartPhase()) :
null;
final List<SubscriptionBaseEvent> events = new ArrayList<SubscriptionBaseEvent>();
events.add(creationEvent);
@@ -522,13 +482,12 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
.setEventPlan(newPlan.getName())
.setEventPlanPhase(currentTimedPhase.getPhase().getName())
.setEventPriceList(newPriceList)
- .setActiveVersion(subscription.getActiveVersion())
.setEffectiveDate(effectiveDate)
.setFromDisk(true));
final TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnChange(subscription, newPlan, newPriceList, effectiveDate, internalTenantContext);
final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
- PhaseEventData.createNextPhaseEvent(subscription.getId(), subscription.getActiveVersion(),
+ PhaseEventData.createNextPhaseEvent(subscription.getId(),
nextTimedPhase.getPhase().getName(), nextTimedPhase.getStartPhase()) :
null;
@@ -553,7 +512,6 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
final List<SubscriptionBaseEvent> cancelEvents = new ArrayList<SubscriptionBaseEvent>();
final SubscriptionBaseEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
.setSubscriptionId(subscription.getId())
- .setActiveVersion(subscription.getActiveVersion())
.setEffectiveDate(effectiveDate)
.setFromDisk(true));
cancelEvents.add(cancelEvent);
@@ -609,7 +567,6 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
//
final SubscriptionBaseEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
.setSubscriptionId(cur.getId())
- .setActiveVersion(cur.getActiveVersion())
.setEffectiveDate(effectiveDate)
.setFromDisk(true));
subscriptionsToBeCancelled.add(cur);
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseTransitionDataIterator.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseTransitionDataIterator.java
index 881e24f..b6402d3 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseTransitionDataIterator.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseTransitionDataIterator.java
@@ -81,10 +81,6 @@ public class SubscriptionBaseTransitionDataIterator implements Iterator<Subscrip
if (visibility == Visibility.FROM_DISK_ONLY && ! ((SubscriptionBaseTransitionData) input).isFromDisk()) {
return true;
}
- if ((kind == Kind.SUBSCRIPTION && shouldSkipForSubscriptionEvents((SubscriptionBaseTransitionData) input)) ||
- (kind == Kind.BILLING && shouldSkipForBillingEvents((SubscriptionBaseTransitionData) input))) {
- return true;
- }
if ((timeLimit == TimeLimit.FUTURE_ONLY && !input.getEffectiveTransitionTime().isAfter(clock.getUTCNow())) ||
((timeLimit == TimeLimit.PAST_OR_PRESENT_ONLY && input.getEffectiveTransitionTime().isAfter(clock.getUTCNow())))) {
return true;
@@ -92,17 +88,6 @@ public class SubscriptionBaseTransitionDataIterator implements Iterator<Subscrip
return false;
}
- private boolean shouldSkipForSubscriptionEvents(final SubscriptionBaseTransitionData input) {
- // SubscriptionBase system knows about all events except for MIGRATE_BILLING
- return (input.getTransitionType() == SubscriptionBaseTransitionType.MIGRATE_BILLING);
- }
-
- private boolean shouldSkipForBillingEvents(final SubscriptionBaseTransitionData input) {
- // Junction system knows about all events except for MIGRATE_ENTITLEMENT
- return input.getTransitionType() == SubscriptionBaseTransitionType.MIGRATE_ENTITLEMENT;
- }
-
-
@Override
public SubscriptionBaseTransition next() {
return next;
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBuilder.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBuilder.java
index a23502f..7beabf6 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBuilder.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBuilder.java
@@ -32,12 +32,11 @@ public class SubscriptionBuilder {
private DateTime updatedDate;
private DateTime alignStartDate;
private DateTime bundleStartDate;
- private Long activeVersion;
private ProductCategory category;
private DateTime chargedThroughDate;
+ private boolean migrated;
public SubscriptionBuilder() {
- this.activeVersion = SubscriptionEvents.INITIAL_VERSION;
}
public SubscriptionBuilder(final DefaultSubscriptionBase original) {
@@ -46,8 +45,8 @@ public class SubscriptionBuilder {
this.alignStartDate = original.getAlignStartDate();
this.bundleStartDate = original.getBundleStartDate();
this.category = original.getCategory();
- this.activeVersion = original.getActiveVersion();
this.chargedThroughDate = original.getChargedThroughDate();
+ this.migrated = original.isMigrated();
}
public SubscriptionBuilder setId(final UUID id) {
@@ -79,14 +78,13 @@ public class SubscriptionBuilder {
this.bundleStartDate = bundleStartDate;
return this;
}
-
- public SubscriptionBuilder setActiveVersion(final long activeVersion) {
- this.activeVersion = activeVersion;
+ public SubscriptionBuilder setChargedThroughDate(final DateTime chargedThroughDate) {
+ this.chargedThroughDate = chargedThroughDate;
return this;
}
- public SubscriptionBuilder setChargedThroughDate(final DateTime chargedThroughDate) {
- this.chargedThroughDate = chargedThroughDate;
+ public SubscriptionBuilder setMigrated(final boolean migrated) {
+ this.migrated = migrated;
return this;
}
@@ -118,11 +116,6 @@ public class SubscriptionBuilder {
public DateTime getBundleStartDate() {
return bundleStartDate;
}
-
- public Long getActiveVersion() {
- return activeVersion;
- }
-
public ProductCategory getCategory() {
return category;
}
@@ -131,6 +124,10 @@ public class SubscriptionBuilder {
return chargedThroughDate;
}
+ public boolean isMigrated() {
+ return migrated;
+ }
+
private void checkAllFieldsSet() {
for (final Field cur : SubscriptionBuilder.class.getDeclaredFields()) {
try {
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionEvents.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionEvents.java
index 46bc931..7977dc5 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionEvents.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionEvents.java
@@ -23,49 +23,31 @@ import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
public class SubscriptionEvents {
- public static final long INITIAL_VERSION = 1;
- private final List<SubscriptionBaseEvent> events;
- private long activeVersion;
+ private final List<SubscriptionBaseEvent> events;
public SubscriptionEvents() {
this.events = new LinkedList<SubscriptionBaseEvent>();
- this.activeVersion = INITIAL_VERSION;
}
public void addEvent(final SubscriptionBaseEvent ev) {
events.add(ev);
}
- public List<SubscriptionBaseEvent> getCurrentView() {
- return getViewForVersion(activeVersion);
- }
-
public List<SubscriptionBaseEvent> getViewForVersion(final long version) {
final LinkedList<SubscriptionBaseEvent> result = new LinkedList<SubscriptionBaseEvent>();
for (final SubscriptionBaseEvent cur : events) {
- if (cur.getActiveVersion() == version) {
- result.add(cur);
- }
+ result.add(cur);
}
return result;
}
- public long getActiveVersion() {
- return activeVersion;
- }
-
- public void setActiveVersion(final long activeVersion) {
- this.activeVersion = activeVersion;
- }
-
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("SubscriptionEvents");
- sb.append("{activeVersion=").append(activeVersion);
sb.append(", events=").append(events);
sb.append('}');
return sb.toString();
@@ -82,9 +64,6 @@ public class SubscriptionEvents {
final SubscriptionEvents that = (SubscriptionEvents) o;
- if (activeVersion != that.activeVersion) {
- return false;
- }
if (events != null ? !events.equals(that.events) : that.events != null) {
return false;
}
@@ -95,7 +74,6 @@ public class SubscriptionEvents {
@Override
public int hashCode() {
int result = events != null ? events.hashCode() : 0;
- result = 31 * result + (int) (activeVersion ^ (activeVersion >>> 32));
return result;
}
}
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/core/DefaultSubscriptionBaseService.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/core/DefaultSubscriptionBaseService.java
index 18dd082..5a40d15 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/core/DefaultSubscriptionBaseService.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/core/DefaultSubscriptionBaseService.java
@@ -152,10 +152,6 @@ public class DefaultSubscriptionBaseService implements EventListener, Subscripti
log.warn("Failed to retrieve subscription for id %s", event.getSubscriptionId());
return;
}
- if (subscription.getActiveVersion() > event.getActiveVersion()) {
- // Skip repaired events
- return;
- }
boolean eventSent = false;
if (event.getType() == EventType.PHASE) {
@@ -187,7 +183,7 @@ public class DefaultSubscriptionBaseService implements EventListener, Subscripti
final DateTime now = clock.getUTCNow();
final TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, now, context);
final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
- PhaseEventData.createNextPhaseEvent(subscription.getId(), subscription.getActiveVersion(),
+ PhaseEventData.createNextPhaseEvent(subscription.getId(),
nextTimedPhase.getPhase().getName(), nextTimedPhase.getStartPhase()) :
null;
if (nextPhaseEvent != null) {
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
index 1e6c118..71b5af6 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -47,9 +47,8 @@ import org.killbill.billing.entitlement.api.SubscriptionApiException;
import org.killbill.billing.entity.EntityPersistenceException;
import org.killbill.billing.subscription.api.SubscriptionBase;
import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
-import org.killbill.billing.subscription.api.migration.AccountMigrationData;
-import org.killbill.billing.subscription.api.migration.AccountMigrationData.BundleMigrationData;
-import org.killbill.billing.subscription.api.migration.AccountMigrationData.SubscriptionMigrationData;
+import org.killbill.billing.subscription.api.transfer.BundleTransferData;
+import org.killbill.billing.subscription.api.transfer.SubscriptionTransferData;
import org.killbill.billing.subscription.api.transfer.TransferCancelData;
import org.killbill.billing.subscription.api.user.DefaultEffectiveSubscriptionEvent;
import org.killbill.billing.subscription.api.user.DefaultRequestedSubscriptionEvent;
@@ -73,11 +72,10 @@ import org.killbill.billing.subscription.events.user.ApiEvent;
import org.killbill.billing.subscription.events.user.ApiEventBuilder;
import org.killbill.billing.subscription.events.user.ApiEventCancel;
import org.killbill.billing.subscription.events.user.ApiEventChange;
-import org.killbill.billing.subscription.events.user.ApiEventMigrateBilling;
import org.killbill.billing.subscription.events.user.ApiEventType;
import org.killbill.billing.subscription.exceptions.SubscriptionBaseError;
-import org.killbill.billing.util.UUIDs;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.entity.Pagination;
import org.killbill.billing.util.entity.dao.DefaultPaginationSqlDaoHelper.PaginationIteratorBuilder;
@@ -118,8 +116,8 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
@Inject
public DefaultSubscriptionDao(final IDBI dbi, final Clock clock, final AddonUtils addonUtils,
final NotificationQueueService notificationQueueService, final PersistentBus eventBus, final CatalogService catalogService,
- final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao) {
- super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao), BundleSqlDao.class);
+ final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory) {
+ super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory), BundleSqlDao.class);
this.clock = clock;
this.notificationQueueService = notificationQueueService;
this.addonUtils = addonUtils;
@@ -540,28 +538,6 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
}
@Override
- public void recreateSubscription(final DefaultSubscriptionBase subscription, final List<SubscriptionBaseEvent> recreateEvents, final InternalCallContext context) {
- transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
- @Override
- public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
- final SubscriptionEventSqlDao transactional = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class);
-
- for (final SubscriptionBaseEvent cur : recreateEvents) {
- transactional.create(new SubscriptionEventModelDao(cur), context);
-
- final boolean isBusEvent = cur.getEffectiveDate().compareTo(clock.getUTCNow()) <= 0 && (cur.getType() == EventType.API_USER);
- recordBusOrFutureNotificationFromTransaction(subscription, cur, entitySqlDaoWrapperFactory, isBusEvent, 0, context);
- }
-
- // Notify the Bus of the latest requested change
- notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, recreateEvents.get(recreateEvents.size() - 1), SubscriptionBaseTransitionType.RE_CREATE, context);
-
- return null;
- }
- });
- }
-
- @Override
public void cancelSubscriptionsOnBasePlanEvent(final DefaultSubscriptionBase subscription, final SubscriptionBaseEvent event, final List<DefaultSubscriptionBase> subscriptions, final List<SubscriptionBaseEvent> cancelEvents, final InternalCallContext context) {
transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
@Override
@@ -642,13 +618,9 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
final SubscriptionEventSqlDao transactional = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class);
final UUID subscriptionId = subscription.getId();
- final List<SubscriptionBaseEvent> changeEventsTweakedWithMigrateBilling = reinsertFutureMigrateBillingEventOnChangeFromTransaction(subscriptionId,
- changeEvents,
- entitySqlDaoWrapperFactory,
- context);
cancelFutureEventsFromTransaction(subscriptionId, changeEvents.get(0).getEffectiveDate(), entitySqlDaoWrapperFactory, context);
- for (final SubscriptionBaseEvent cur : changeEventsTweakedWithMigrateBilling) {
+ for (final SubscriptionBaseEvent cur : changeEvents) {
transactional.create(new SubscriptionEventModelDao(cur), context);
final boolean isBusEvent = cur.getEffectiveDate().compareTo(clock.getUTCNow()) <= 0 && (cur.getType() == EventType.API_USER);
@@ -656,7 +628,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
}
// Notify the Bus of the latest requested change
- final SubscriptionBaseEvent finalEvent = changeEventsTweakedWithMigrateBilling.get(changeEvents.size() - 1);
+ final SubscriptionBaseEvent finalEvent = changeEvents.get(changeEvents.size() - 1);
notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, finalEvent, SubscriptionBaseTransitionType.CHANGE, context);
// Cancel associated add-ons
@@ -667,92 +639,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
});
}
- //
- // This piece of code has been isolated in its own method in order to allow for migrated subscriptions to have their plan to changed prior
- // to MIGRATE_BILLING; the effect will be to reflect the change from an subscription point of view while ignoring the change until we hit
- // the begining of the billing, that is when we hit the MIGRATE_BILLING event. If we had a clear separation between subscription and
- // billing that would not be needed.
- //
- // If there is a change of plan prior to a future MIGRATE_BILLING, we want to modify the existing MIGRATE_BILLING so it reflects
- // the new plan, phase, pricelist; Invoice will only see the MIGRATE_BILLING as things prior to that will be ignored, so we need to make sure
- // that event reflects the correct subscription information.
- //
- //
- final List<SubscriptionBaseEvent> reinsertFutureMigrateBillingEventOnChangeFromTransaction(final UUID subscriptionId, final List<SubscriptionBaseEvent> changeEvents, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context) {
- final SubscriptionEventModelDao migrateBillingEvent = findFutureEventFromTransaction(subscriptionId, entitySqlDaoWrapperFactory, EventType.API_USER, ApiEventType.MIGRATE_BILLING, context);
- if (migrateBillingEvent == null) {
- // No future migrate billing : returns same list
- return changeEvents;
- }
- String prevPlan = null;
- String prevPhase = null;
- String prevPriceList = null;
- String curPlan = null;
- String curPhase = null;
- String curPriceList = null;
- for (SubscriptionBaseEvent cur : changeEvents) {
- switch (cur.getType()) {
- case API_USER:
- final ApiEvent apiEvent = (ApiEvent) cur;
- curPlan = apiEvent.getEventPlan();
- curPhase = apiEvent.getEventPlanPhase();
- curPriceList = apiEvent.getPriceList();
- break;
-
- case PHASE:
- final PhaseEvent phaseEvent = (PhaseEvent) cur;
- curPhase = phaseEvent.getPhase();
- break;
-
- default:
- throw new SubscriptionBaseError("Unknown event type " + cur.getType());
- }
-
- if (cur.getEffectiveDate().compareTo(migrateBillingEvent.getEffectiveDate()) > 0) {
- if (cur.getType() == EventType.API_USER && ((ApiEvent) cur).getApiEventType() == ApiEventType.CHANGE) {
- // This is an EOT change that is occurring after the MigrateBilling : returns same list
- return changeEvents;
- }
- // We found the first event after the migrate billing
- break;
- }
- prevPlan = curPlan;
- prevPhase = curPhase;
- prevPriceList = curPriceList;
- }
-
- if (prevPlan != null) {
- // Create the new MIGRATE_BILLING with same effectiveDate but new plan information
- final DateTime now = clock.getUTCNow();
- final ApiEventBuilder builder = new ApiEventBuilder()
- .setActive(true)
- .setApiEventType(ApiEventType.MIGRATE_BILLING)
- .setFromDisk(true)
- .setTotalOrdering(migrateBillingEvent.getTotalOrdering())
- .setUuid(UUIDs.randomUUID())
- .setSubscriptionId(migrateBillingEvent.getSubscriptionId())
- .setCreatedDate(now)
- .setUpdatedDate(now)
- .setEffectiveDate(migrateBillingEvent.getEffectiveDate())
- .setActiveVersion(migrateBillingEvent.getCurrentVersion())
- .setEventPlan(prevPlan)
- .setEventPlanPhase(prevPhase)
- .setEventPriceList(prevPriceList);
-
- final SubscriptionBaseEvent newMigrateBillingEvent = new ApiEventMigrateBilling(builder);
- changeEvents.add(newMigrateBillingEvent);
-
- Collections.sort(changeEvents, new Comparator<SubscriptionBaseEvent>() {
- @Override
- public int compare(final SubscriptionBaseEvent o1, final SubscriptionBaseEvent o2) {
- return o1.getEffectiveDate().compareTo(o2.getEffectiveDate());
- }
- });
- }
-
- return changeEvents;
- }
private List<SubscriptionBaseEvent> filterSubscriptionBaseEvents(final List<SubscriptionEventModelDao> models) {
final Collection<SubscriptionEventModelDao> filteredModels = Collections2.filter(models, new Predicate<SubscriptionEventModelDao>() {
@@ -938,7 +825,6 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
final DateTime now = clock.getUTCNow();
final SubscriptionBaseEvent addOnCancelEvent = new ApiEventCancel(new ApiEventBuilder()
.setSubscriptionId(reloaded.getId())
- .setActiveVersion(((DefaultSubscriptionBase) reloaded).getActiveVersion())
.setEffectiveDate(baseTriggerEventForAddOnCancellation.getEffectiveDate())
.setCreatedDate(baseTriggerEventForAddOnCancellation.getCreatedDate())
// This event is only there to indicate the ADD_ON is future canceled, but it is not there
@@ -993,22 +879,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
}
@Override
- public void migrate(final UUID accountId, final AccountMigrationData accountData, final InternalCallContext context) {
- transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
- @Override
- public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
- final SubscriptionEventSqlDao transactional = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class);
-
- for (final BundleMigrationData curBundle : accountData.getData()) {
- migrateBundleDataFromTransaction(curBundle, transactional, entitySqlDaoWrapperFactory, context);
- }
- return null;
- }
- });
- }
-
- @Override
- public void transfer(final UUID srcAccountId, final UUID destAccountId, final BundleMigrationData bundleTransferData,
+ public void transfer(final UUID srcAccountId, final UUID destAccountId, final BundleTransferData bundleTransferData,
final List<TransferCancelData> transferCancelData, final InternalCallContext fromContext, final InternalCallContext toContext) {
transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
@@ -1021,7 +892,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
cancelSubscriptionFromTransaction(cancel.getSubscription(), cancel.getCancelEvent(), entitySqlDaoWrapperFactory, fromContext, 0);
}
- migrateBundleDataFromTransaction(bundleTransferData, transactional, entitySqlDaoWrapperFactory, toContext);
+ transferBundleDataFromTransaction(bundleTransferData, transactional, entitySqlDaoWrapperFactory, toContext);
return null;
}
});
@@ -1123,8 +994,8 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
}
}
- private void migrateBundleDataFromTransaction(final BundleMigrationData bundleTransferData, final SubscriptionEventSqlDao transactional,
- final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context) throws EntityPersistenceException {
+ private void transferBundleDataFromTransaction(final BundleTransferData bundleTransferData, final SubscriptionEventSqlDao transactional,
+ final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context) throws EntityPersistenceException {
final SubscriptionSqlDao transSubDao = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class);
final BundleSqlDao transBundleDao = entitySqlDaoWrapperFactory.become(BundleSqlDao.class);
@@ -1137,7 +1008,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
return;
}
- for (final SubscriptionMigrationData curSubscription : bundleTransferData.getSubscriptions()) {
+ for (final SubscriptionTransferData curSubscription : bundleTransferData.getSubscriptions()) {
final DefaultSubscriptionBase subData = curSubscription.getData();
for (final SubscriptionBaseEvent curEvent : curSubscription.getInitialEvents()) {
transactional.create(new SubscriptionEventModelDao(curEvent), context);
@@ -1150,7 +1021,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
// Notify the Bus of the latest requested change
final SubscriptionBaseEvent finalEvent = curSubscription.getInitialEvents().get(curSubscription.getInitialEvents().size() - 1);
- notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subData, finalEvent, SubscriptionBaseTransitionType.MIGRATE_BILLING, context);
+ notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subData, finalEvent, SubscriptionBaseTransitionType.TRANSFER, context);
}
transBundleDao.create(new SubscriptionBundleModelDao(bundleData), context);
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionEventModelDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionEventModelDao.java
index 73d4492..9bcdd69 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionEventModelDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionEventModelDao.java
@@ -25,18 +25,9 @@ import org.killbill.billing.subscription.events.SubscriptionBaseEvent.EventType;
import org.killbill.billing.subscription.events.EventBaseBuilder;
import org.killbill.billing.subscription.events.phase.PhaseEvent;
import org.killbill.billing.subscription.events.phase.PhaseEventBuilder;
-import org.killbill.billing.subscription.events.phase.PhaseEventData;
import org.killbill.billing.subscription.events.user.ApiEvent;
import org.killbill.billing.subscription.events.user.ApiEventBuilder;
-import org.killbill.billing.subscription.events.user.ApiEventCancel;
-import org.killbill.billing.subscription.events.user.ApiEventChange;
-import org.killbill.billing.subscription.events.user.ApiEventCreate;
-import org.killbill.billing.subscription.events.user.ApiEventMigrateBilling;
-import org.killbill.billing.subscription.events.user.ApiEventMigrateSubscription;
-import org.killbill.billing.subscription.events.user.ApiEventReCreate;
-import org.killbill.billing.subscription.events.user.ApiEventTransfer;
import org.killbill.billing.subscription.events.user.ApiEventType;
-import org.killbill.billing.subscription.events.user.ApiEventUncancel;
import org.killbill.billing.subscription.exceptions.SubscriptionBaseError;
import org.killbill.billing.util.dao.TableName;
import org.killbill.billing.util.entity.dao.EntityModelDao;
@@ -47,13 +38,11 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
private long totalOrdering;
private EventType eventType;
private ApiEventType userType;
- private DateTime requestedDate; // deprecated (similar to effectiveDate)
private DateTime effectiveDate;
private UUID subscriptionId;
private String planName;
private String phaseName;
private String priceListName;
- private long currentVersion;
private boolean isActive;
public SubscriptionEventModelDao() {
@@ -61,20 +50,18 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
}
public SubscriptionEventModelDao(final UUID id, final long totalOrdering, final EventType eventType, final ApiEventType userType,
- final DateTime requestedDate, final DateTime effectiveDate, final UUID subscriptionId,
- final String planName, final String phaseName, final String priceListName, final long currentVersion,
+ final DateTime effectiveDate, final UUID subscriptionId,
+ final String planName, final String phaseName, final String priceListName,
final boolean active, final DateTime createDate, final DateTime updateDate) {
super(id, createDate, updateDate);
this.totalOrdering = totalOrdering;
this.eventType = eventType;
this.userType = userType;
- this.requestedDate = requestedDate;
this.effectiveDate = effectiveDate;
this.subscriptionId = subscriptionId;
this.planName = planName;
this.phaseName = phaseName;
this.priceListName = priceListName;
- this.currentVersion = currentVersion;
this.isActive = active;
}
@@ -83,13 +70,11 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
this.totalOrdering = src.getTotalOrdering();
this.eventType = src.getType();
this.userType = eventType == EventType.API_USER ? ((ApiEvent) src).getApiEventType() : null;
- this.requestedDate = src.getEffectiveDate();
this.effectiveDate = src.getEffectiveDate();
this.subscriptionId = src.getSubscriptionId();
this.planName = eventType == EventType.API_USER ? ((ApiEvent) src).getEventPlan() : null;
this.phaseName = eventType == EventType.API_USER ? ((ApiEvent) src).getEventPlanPhase() : ((PhaseEvent) src).getPhase();
this.priceListName = eventType == EventType.API_USER ? ((ApiEvent) src).getPriceList() : null;
- this.currentVersion = src.getActiveVersion();
this.isActive = src.isActive();
}
@@ -105,10 +90,6 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
return userType;
}
- public DateTime getRequestedDate() {
- return requestedDate;
- }
-
public DateTime getEffectiveDate() {
return effectiveDate;
}
@@ -129,10 +110,6 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
return priceListName;
}
- public long getCurrentVersion() {
- return currentVersion;
- }
-
// TODO required for jdbi binder
public boolean getIsActive() {
return isActive;
@@ -154,10 +131,6 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
this.userType = userType;
}
- public void setRequestedDate(final DateTime requestedDate) {
- this.requestedDate = requestedDate;
- }
-
public void setEffectiveDate(final DateTime effectiveDate) {
this.effectiveDate = effectiveDate;
}
@@ -178,10 +151,6 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
this.priceListName = priceListName;
}
- public void setCurrentVersion(final long currentVersion) {
- this.currentVersion = currentVersion;
- }
-
public void setIsActive(final boolean isActive) {
this.isActive = isActive;
}
@@ -201,7 +170,6 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
.setCreatedDate(src.getCreatedDate())
.setUpdatedDate(src.getUpdatedDate())
.setEffectiveDate(src.getEffectiveDate())
- .setActiveVersion(src.getCurrentVersion())
.setActive(src.isActive());
SubscriptionBaseEvent result;
@@ -229,13 +197,11 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
sb.append("{totalOrdering=").append(totalOrdering);
sb.append(", eventType=").append(eventType);
sb.append(", userType=").append(userType);
- sb.append(", requestedDate=").append(requestedDate);
sb.append(", effectiveDate=").append(effectiveDate);
sb.append(", subscriptionId=").append(subscriptionId);
sb.append(", planName='").append(planName).append('\'');
sb.append(", phaseName='").append(phaseName).append('\'');
sb.append(", priceListName='").append(priceListName).append('\'');
- sb.append(", currentVersion=").append(currentVersion);
sb.append(", isActive=").append(isActive);
sb.append('}');
return sb.toString();
@@ -255,9 +221,6 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
final SubscriptionEventModelDao that = (SubscriptionEventModelDao) o;
- if (currentVersion != that.currentVersion) {
- return false;
- }
if (isActive != that.isActive) {
return false;
}
@@ -279,9 +242,6 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
if (priceListName != null ? !priceListName.equals(that.priceListName) : that.priceListName != null) {
return false;
}
- if (requestedDate != null ? !requestedDate.equals(that.requestedDate) : that.requestedDate != null) {
- return false;
- }
if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null) {
return false;
}
@@ -298,13 +258,11 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
result = 31 * result + (int) (totalOrdering ^ (totalOrdering >>> 32));
result = 31 * result + (eventType != null ? eventType.hashCode() : 0);
result = 31 * result + (userType != null ? userType.hashCode() : 0);
- result = 31 * result + (requestedDate != null ? requestedDate.hashCode() : 0);
result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
result = 31 * result + (subscriptionId != null ? subscriptionId.hashCode() : 0);
result = 31 * result + (planName != null ? planName.hashCode() : 0);
result = 31 * result + (phaseName != null ? phaseName.hashCode() : 0);
result = 31 * result + (priceListName != null ? priceListName.hashCode() : 0);
- result = 31 * result + (int) (currentVersion ^ (currentVersion >>> 32));
result = 31 * result + (isActive ? 1 : 0);
return result;
}
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionModelDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionModelDao.java
index d9d778c..b53a9cd 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionModelDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionModelDao.java
@@ -35,25 +35,25 @@ public class SubscriptionModelDao extends EntityModelDaoBase implements EntityMo
private ProductCategory category;
private DateTime startDate;
private DateTime bundleStartDate;
- private long activeVersion;
private DateTime chargedThroughDate;
+ private boolean migrated;
public SubscriptionModelDao() { /* For the DAO mapper */ }
public SubscriptionModelDao(final UUID id, final UUID bundleId, final ProductCategory category, final DateTime startDate, final DateTime bundleStartDate,
- final long activeVersion, final DateTime chargedThroughDate, final DateTime createdDate, final DateTime updateDate) {
+ final DateTime chargedThroughDate, final boolean migrated, final DateTime createdDate, final DateTime updateDate) {
super(id, createdDate, updateDate);
this.bundleId = bundleId;
this.category = category;
this.startDate = startDate;
this.bundleStartDate = bundleStartDate;
- this.activeVersion = activeVersion;
this.chargedThroughDate = chargedThroughDate;
+ this.migrated = migrated;
}
public SubscriptionModelDao(final DefaultSubscriptionBase src) {
- this(src.getId(), src.getBundleId(), src.getCategory(), src.getAlignStartDate(), src.getBundleStartDate(), src.getActiveVersion(),
- src.getChargedThroughDate(), src.getCreatedDate(), src.getUpdatedDate());
+ this(src.getId(), src.getBundleId(), src.getCategory(), src.getAlignStartDate(), src.getBundleStartDate(),
+ src.getChargedThroughDate(), src.isMigrated(), src.getCreatedDate(), src.getUpdatedDate());
}
public UUID getBundleId() {
@@ -72,9 +72,6 @@ public class SubscriptionModelDao extends EntityModelDaoBase implements EntityMo
return bundleStartDate;
}
- public long getActiveVersion() {
- return activeVersion;
- }
public DateTime getChargedThroughDate() {
return chargedThroughDate;
@@ -96,14 +93,19 @@ public class SubscriptionModelDao extends EntityModelDaoBase implements EntityMo
this.bundleStartDate = bundleStartDate;
}
- public void setActiveVersion(final long activeVersion) {
- this.activeVersion = activeVersion;
- }
public void setChargedThroughDate(final DateTime chargedThroughDate) {
this.chargedThroughDate = chargedThroughDate;
}
+ public boolean isMigrated() {
+ return migrated;
+ }
+
+ public void setMigrated(final boolean migrated) {
+ this.migrated = migrated;
+ }
+
public static SubscriptionBase toSubscription(final SubscriptionModelDao src) {
if (src == null) {
return null;
@@ -116,8 +118,8 @@ public class SubscriptionModelDao extends EntityModelDaoBase implements EntityMo
.setUpdatedDate(src.getUpdatedDate())
.setBundleStartDate(src.getBundleStartDate())
.setAlignStartDate(src.getStartDate())
- .setActiveVersion(src.getActiveVersion())
.setChargedThroughDate(src.getChargedThroughDate())
+ .setMigrated(src.isMigrated())
.setCreatedDate(src.getCreatedDate())
.setUpdatedDate(src.getUpdatedDate()));
}
@@ -130,8 +132,8 @@ public class SubscriptionModelDao extends EntityModelDaoBase implements EntityMo
sb.append(", category=").append(category);
sb.append(", startDate=").append(startDate);
sb.append(", bundleStartDate=").append(bundleStartDate);
- sb.append(", activeVersion=").append(activeVersion);
sb.append(", chargedThroughDate=").append(chargedThroughDate);
+ sb.append(", migrated=").append(migrated);
sb.append('}');
return sb.toString();
}
@@ -150,9 +152,6 @@ public class SubscriptionModelDao extends EntityModelDaoBase implements EntityMo
final SubscriptionModelDao that = (SubscriptionModelDao) o;
- if (activeVersion != that.activeVersion) {
- return false;
- }
if (bundleId != null ? !bundleId.equals(that.bundleId) : that.bundleId != null) {
return false;
}
@@ -165,6 +164,9 @@ public class SubscriptionModelDao extends EntityModelDaoBase implements EntityMo
if (chargedThroughDate != null ? !chargedThroughDate.equals(that.chargedThroughDate) : that.chargedThroughDate != null) {
return false;
}
+ if (migrated != that.migrated) {
+ return false;
+ }
if (startDate != null ? !startDate.equals(that.startDate) : that.startDate != null) {
return false;
}
@@ -179,8 +181,8 @@ public class SubscriptionModelDao extends EntityModelDaoBase implements EntityMo
result = 31 * result + (category != null ? category.hashCode() : 0);
result = 31 * result + (startDate != null ? startDate.hashCode() : 0);
result = 31 * result + (bundleStartDate != null ? bundleStartDate.hashCode() : 0);
- result = 31 * result + (int) (activeVersion ^ (activeVersion >>> 32));
result = 31 * result + (chargedThroughDate != null ? chargedThroughDate.hashCode() : 0);
+ result = 31 * result + Boolean.valueOf(migrated).hashCode();
return result;
}
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionDao.java
index 487fae3..8003503 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionDao.java
@@ -25,8 +25,7 @@ import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.CatalogApiException;
import org.killbill.billing.entitlement.api.SubscriptionApiException;
import org.killbill.billing.subscription.api.SubscriptionBase;
-import org.killbill.billing.subscription.api.migration.AccountMigrationData;
-import org.killbill.billing.subscription.api.migration.AccountMigrationData.BundleMigrationData;
+import org.killbill.billing.subscription.api.transfer.BundleTransferData;
import org.killbill.billing.subscription.api.transfer.TransferCancelData;
import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
import org.killbill.billing.subscription.api.user.DefaultSubscriptionBaseBundle;
@@ -84,8 +83,6 @@ public interface SubscriptionDao extends EntityDao<SubscriptionBundleModelDao, S
public void createSubscriptionWithAddOns(List<DefaultSubscriptionBase> subscriptions, Map<UUID, List<SubscriptionBaseEvent>> initialEventsMap, InternalCallContext context);
- public void recreateSubscription(DefaultSubscriptionBase subscription, List<SubscriptionBaseEvent> recreateEvents, InternalCallContext context);
-
public void cancelSubscriptionsOnBasePlanEvent(DefaultSubscriptionBase subscription, SubscriptionBaseEvent event, List<DefaultSubscriptionBase> subscriptions, List<SubscriptionBaseEvent> cancelEvents, InternalCallContext context);
public void cancelSubscriptions(List<DefaultSubscriptionBase> subscriptions, List<SubscriptionBaseEvent> cancelEvents, InternalCallContext context);
@@ -94,9 +91,7 @@ public interface SubscriptionDao extends EntityDao<SubscriptionBundleModelDao, S
public void changePlan(DefaultSubscriptionBase subscription, List<SubscriptionBaseEvent> changeEvents, List<DefaultSubscriptionBase> subscriptionsToBeCancelled, List<SubscriptionBaseEvent> cancelEvents, InternalCallContext context);
- public void migrate(UUID accountId, AccountMigrationData data, InternalCallContext context);
-
- public void transfer(UUID srcAccountId, UUID destAccountId, BundleMigrationData data, List<TransferCancelData> transferCancelData, InternalCallContext fromContext, InternalCallContext toContext);
+ public void transfer(UUID srcAccountId, UUID destAccountId, BundleTransferData data, List<TransferCancelData> transferCancelData, InternalCallContext fromContext, InternalCallContext toContext);
public void updateBundleExternalKey(UUID bundleId, String externalKey, InternalCallContext context);
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.java
index 177df8b..33358f6 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.java
@@ -41,17 +41,6 @@ public interface SubscriptionEventSqlDao extends EntitySqlDao<SubscriptionEventM
public void unactiveEvent(@Bind("id") String id,
@BindBean final InternalCallContext context);
- @SqlUpdate
- @Audited(ChangeType.UPDATE)
- public void reactiveEvent(@Bind("id") String id,
- @BindBean final InternalCallContext context);
-
- @SqlUpdate
- @Audited(ChangeType.UPDATE)
- public void updateVersion(@Bind("id") String id,
- @Bind("currentVersion") Long currentVersion,
- @BindBean final InternalCallContext context);
-
@SqlQuery
public List<SubscriptionEventModelDao> getFutureActiveEventForSubscription(@Bind("subscriptionId") String subscriptionId,
@Bind("now") Date now,
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionSqlDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionSqlDao.java
index 76baf12..b47b5ba 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionSqlDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionSqlDao.java
@@ -45,15 +45,4 @@ public interface SubscriptionSqlDao extends EntitySqlDao<SubscriptionModelDao, S
public void updateChargedThroughDate(@Bind("id") String id, @Bind("chargedThroughDate") Date chargedThroughDate,
@BindBean final InternalCallContext context);
- @SqlUpdate
- @Audited(ChangeType.UPDATE)
- void updateActiveVersion(@Bind("id") String id, @Bind("activeVersion") long activeVersion,
- @BindBean final InternalCallContext context);
-
- @SqlUpdate
- @Audited(ChangeType.UPDATE)
- public void updateForRepair(@Bind("id") String id, @Bind("activeVersion") long activeVersion,
- @Bind("startDate") Date startDate,
- @Bind("bundleStartDate") Date bundleStartDate,
- @BindBean final InternalCallContext context);
}
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/events/EventBase.java b/subscription/src/main/java/org/killbill/billing/subscription/events/EventBase.java
index ed379f8..334d524 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/events/EventBase.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/events/EventBase.java
@@ -31,7 +31,6 @@ public abstract class EventBase implements SubscriptionBaseEvent {
private final DateTime effectiveDate;
private final long totalOrdering;
- private final long activeVersion;
private final boolean isActive;
public EventBase(final EventBaseBuilder<?> builder) {
@@ -41,7 +40,6 @@ public abstract class EventBase implements SubscriptionBaseEvent {
this.createdDate = builder.getCreatedDate();
this.updatedDate = builder.getUpdatedDate();
this.effectiveDate = builder.getEffectiveDate();
- this.activeVersion = builder.getActiveVersion();
this.isActive = builder.isActive();
}
@@ -76,11 +74,6 @@ public abstract class EventBase implements SubscriptionBaseEvent {
}
@Override
- public long getActiveVersion() {
- return activeVersion;
- }
-
- @Override
public boolean isActive() {
return isActive;
}
@@ -89,7 +82,7 @@ public abstract class EventBase implements SubscriptionBaseEvent {
// Really used for unit tests only as the sql implementation relies on date first and then event insertion
//
// Order first by:
- // - effectiveDate, followed by processedDate, requestedDate
+ // - effectiveDate, followed by processedDate
// - if all dates are equal-- unlikely, we first return PHASE EVENTS
// - If both events are User events, return the first CREATE, CHANGE,... as specified by ApiEventType
// - If all that is not enough return consistent by random ordering based on UUID
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/events/EventBaseBuilder.java b/subscription/src/main/java/org/killbill/billing/subscription/events/EventBaseBuilder.java
index 4dd7b96..41fd074 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/events/EventBaseBuilder.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/events/EventBaseBuilder.java
@@ -31,7 +31,6 @@ public abstract class EventBaseBuilder<T extends EventBaseBuilder<T>> {
private DateTime updatedDate;
private DateTime effectiveDate;
- private long activeVersion;
private boolean isActive;
public EventBaseBuilder() {
@@ -46,7 +45,6 @@ public abstract class EventBaseBuilder<T extends EventBaseBuilder<T>> {
this.effectiveDate = event.getEffectiveDate();
this.createdDate = event.getCreatedDate();
this.updatedDate = event.getUpdatedDate();
- this.activeVersion = event.getActiveVersion();
this.isActive = event.isActive();
this.totalOrdering = event.getTotalOrdering();
}
@@ -57,7 +55,6 @@ public abstract class EventBaseBuilder<T extends EventBaseBuilder<T>> {
this.effectiveDate = copy.effectiveDate;
this.createdDate = copy.getCreatedDate();
this.updatedDate = copy.getUpdatedDate();
- this.activeVersion = copy.activeVersion;
this.isActive = copy.isActive;
this.totalOrdering = copy.totalOrdering;
}
@@ -92,11 +89,6 @@ public abstract class EventBaseBuilder<T extends EventBaseBuilder<T>> {
return (T) this;
}
- public T setActiveVersion(final long activeVersion) {
- this.activeVersion = activeVersion;
- return (T) this;
- }
-
public T setActive(final boolean isActive) {
this.isActive = isActive;
return (T) this;
@@ -125,11 +117,6 @@ public abstract class EventBaseBuilder<T extends EventBaseBuilder<T>> {
public DateTime getEffectiveDate() {
return effectiveDate;
}
-
- public long getActiveVersion() {
- return activeVersion;
- }
-
public boolean isActive() {
return isActive;
}
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/events/phase/PhaseEventData.java b/subscription/src/main/java/org/killbill/billing/subscription/events/phase/PhaseEventData.java
index 07afcdf..acf9375 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/events/phase/PhaseEventData.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/events/phase/PhaseEventData.java
@@ -21,7 +21,6 @@ import java.util.UUID;
import org.joda.time.DateTime;
-import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
import org.killbill.billing.subscription.events.EventBase;
@@ -51,18 +50,16 @@ public class PhaseEventData extends EventBase implements PhaseEvent {
+ ", getType()=" + getType()
+ ", getPhase()=" + getPhase()
+ ", getEffectiveDate()=" + getEffectiveDate()
- + ", getActiveVersion()=" + getActiveVersion()
+ ", getSubscriptionId()=" + getSubscriptionId()
+ ", isActive()=" + isActive() + "]\n";
}
- public static PhaseEvent createNextPhaseEvent(final UUID subscriptionId, final long activeVersion, final String phaseName, final DateTime effectiveDate) {
+ public static PhaseEvent createNextPhaseEvent(final UUID subscriptionId, final String phaseName, final DateTime effectiveDate) {
return (phaseName == null) ?
null :
new PhaseEventData(new PhaseEventBuilder()
.setSubscriptionId(subscriptionId)
.setEffectiveDate(effectiveDate)
- .setActiveVersion(activeVersion)
.setPhaseName(phaseName));
}
}
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/events/SubscriptionBaseEvent.java b/subscription/src/main/java/org/killbill/billing/subscription/events/SubscriptionBaseEvent.java
index 1f81e63..f710d44 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/events/SubscriptionBaseEvent.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/events/SubscriptionBaseEvent.java
@@ -34,8 +34,6 @@ public interface SubscriptionBaseEvent extends Comparable<SubscriptionBaseEvent>
public long getTotalOrdering();
- public long getActiveVersion();
-
public boolean isActive();
public DateTime getEffectiveDate();
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventBase.java b/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventBase.java
index 98f6b9e..70c5d63 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventBase.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventBase.java
@@ -78,7 +78,6 @@ public class ApiEventBase extends EventBase implements ApiEvent {
+ ", getEventPlanPhase()=" + getEventPlanPhase()
+ ", getType()=" + getType()
+ ", getEffectiveDate()=" + getEffectiveDate()
- + ", getActiveVersion()=" + getActiveVersion()
+ ", getSubscriptionId()=" + getSubscriptionId()
+ ", isActive()=" + isActive() + "]";
}
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventBuilder.java b/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventBuilder.java
index 18cedb6..7e3ce7a 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventBuilder.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventBuilder.java
@@ -92,20 +92,12 @@ public class ApiEventBuilder extends EventBaseBuilder<ApiEventBuilder> {
final ApiEventBase result;
if (apiEventType == ApiEventType.CREATE) {
result = new ApiEventCreate(this);
- } else if (apiEventType == ApiEventType.RE_CREATE) {
- result = new ApiEventReCreate(this);
- } else if (apiEventType == ApiEventType.MIGRATE_ENTITLEMENT) {
- result = new ApiEventMigrateSubscription(this);
- } else if (apiEventType == ApiEventType.MIGRATE_BILLING) {
- result = new ApiEventMigrateBilling(this);
} else if (apiEventType == ApiEventType.TRANSFER) {
result = new ApiEventTransfer(this);
} else if (apiEventType == ApiEventType.CHANGE) {
result = new ApiEventChange(this);
} else if (apiEventType == ApiEventType.CANCEL) {
result = new ApiEventCancel(this);
- } else if (apiEventType == ApiEventType.RE_CREATE) {
- result = new ApiEventReCreate(this);
} else if (apiEventType == ApiEventType.UNCANCEL) {
result = new ApiEventUncancel(this);
} else {
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventType.java b/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventType.java
index 4006a7a..1646243 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventType.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventType.java
@@ -20,24 +20,12 @@ import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
public enum ApiEventType {
- MIGRATE_ENTITLEMENT {
- @Override
- public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
- return SubscriptionBaseTransitionType.MIGRATE_ENTITLEMENT;
- }
- },
CREATE {
@Override
public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
return SubscriptionBaseTransitionType.CREATE;
}
},
- MIGRATE_BILLING {
- @Override
- public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
- return SubscriptionBaseTransitionType.MIGRATE_BILLING;
- }
- },
TRANSFER {
@Override
public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
@@ -50,12 +38,6 @@ public enum ApiEventType {
return SubscriptionBaseTransitionType.CHANGE;
}
},
- RE_CREATE {
- @Override
- public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
- return SubscriptionBaseTransitionType.RE_CREATE;
- }
- },
CANCEL {
@Override
public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/glue/DefaultSubscriptionModule.java b/subscription/src/main/java/org/killbill/billing/subscription/glue/DefaultSubscriptionModule.java
index e0ba5ab..f5ac919 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/glue/DefaultSubscriptionModule.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/glue/DefaultSubscriptionModule.java
@@ -20,13 +20,10 @@ package org.killbill.billing.subscription.glue;
import org.killbill.billing.glue.SubscriptionModule;
import org.killbill.billing.platform.api.KillbillConfigSource;
-import org.killbill.billing.subscription.alignment.MigrationPlanAligner;
import org.killbill.billing.subscription.alignment.PlanAligner;
import org.killbill.billing.subscription.api.SubscriptionBaseApiService;
import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
import org.killbill.billing.subscription.api.SubscriptionBaseService;
-import org.killbill.billing.subscription.api.migration.DefaultSubscriptionBaseMigrationApi;
-import org.killbill.billing.subscription.api.migration.SubscriptionBaseMigrationApi;
import org.killbill.billing.subscription.api.svcs.DefaultSubscriptionInternalApi;
import org.killbill.billing.subscription.api.timeline.DefaultSubscriptionBaseTimelineApi;
import org.killbill.billing.subscription.api.timeline.SubscriptionBaseTimelineApi;
@@ -61,11 +58,8 @@ public class DefaultSubscriptionModule extends KillBillModule implements Subscri
bind(DefaultSubscriptionBaseService.class).asEagerSingleton();
bind(PlanAligner.class).asEagerSingleton();
bind(AddonUtils.class).asEagerSingleton();
- bind(MigrationPlanAligner.class).asEagerSingleton();
-
installSubscriptionService();
installSubscriptionTimelineApi();
- installSubscriptionMigrationApi();
installSubscriptionInternalApi();
installSubscriptionTransferApi();
}
@@ -88,11 +82,6 @@ public class DefaultSubscriptionModule extends KillBillModule implements Subscri
}
@Override
- public void installSubscriptionMigrationApi() {
- bind(SubscriptionBaseMigrationApi.class).to(DefaultSubscriptionBaseMigrationApi.class).asEagerSingleton();
- }
-
- @Override
public void installSubscriptionInternalApi() {
bind(SubscriptionBaseInternalApi.class).to(DefaultSubscriptionInternalApi.class).asEagerSingleton();
}
diff --git a/subscription/src/main/resources/org/killbill/billing/subscription/ddl.sql b/subscription/src/main/resources/org/killbill/billing/subscription/ddl.sql
index 30137f4..d10805e 100644
--- a/subscription/src/main/resources/org/killbill/billing/subscription/ddl.sql
+++ b/subscription/src/main/resources/org/killbill/billing/subscription/ddl.sql
@@ -6,13 +6,11 @@ CREATE TABLE subscription_events (
id varchar(36) NOT NULL,
event_type varchar(9) NOT NULL,
user_type varchar(25) DEFAULT NULL,
- requested_date datetime NOT NULL,
effective_date datetime NOT NULL,
subscription_id varchar(36) NOT NULL,
- plan_name varchar(64) DEFAULT NULL,
- phase_name varchar(128) DEFAULT NULL,
+ plan_name varchar(255) DEFAULT NULL,
+ phase_name varchar(255) DEFAULT NULL,
price_list_name varchar(64) DEFAULT NULL,
- current_version int DEFAULT 1,
is_active boolean default true,
created_by varchar(50) NOT NULL,
created_date datetime NOT NULL,
@@ -24,7 +22,7 @@ CREATE TABLE subscription_events (
) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
CREATE UNIQUE INDEX subscription_events_id ON subscription_events(id);
CREATE INDEX idx_ent_1 ON subscription_events(subscription_id, is_active, effective_date);
-CREATE INDEX idx_ent_2 ON subscription_events(subscription_id, effective_date, created_date, requested_date,id);
+CREATE INDEX idx_ent_2 ON subscription_events(subscription_id, effective_date, created_date, id);
CREATE INDEX subscription_events_tenant_account_record_id ON subscription_events(tenant_record_id, account_record_id);
DROP TABLE IF EXISTS subscriptions;
@@ -35,8 +33,8 @@ CREATE TABLE subscriptions (
category varchar(32) NOT NULL,
start_date datetime NOT NULL,
bundle_start_date datetime NOT NULL,
- active_version int DEFAULT 1,
charged_through_date datetime DEFAULT NULL,
+ migrated bool NOT NULL,
created_by varchar(50) NOT NULL,
created_date datetime NOT NULL,
updated_by varchar(50) NOT NULL,
diff --git a/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.sql.stg b/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.sql.stg
index db16f4a..296083c 100644
--- a/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.sql.stg
+++ b/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.sql.stg
@@ -16,13 +16,11 @@ order by <prefix>effective_date ASC, <recordIdField(prefix)> ASC
tableFields(prefix) ::= <<
<prefix> event_type
, <prefix> user_type
-, <prefix> requested_date
, <prefix> effective_date
, <prefix> subscription_id
, <prefix> plan_name
, <prefix> phase_name
, <prefix> price_list_name
-, <prefix> current_version
, <prefix> is_active
, <prefix> created_by
, <prefix> created_date
@@ -33,13 +31,11 @@ tableFields(prefix) ::= <<
tableValues() ::= <<
:eventType
, :userType
-, :requestedDate
, :effectiveDate
, :subscriptionId
, :planName
, :phaseName
, :priceListName
-, :currentVersion
, :isActive
, :createdBy
, :createdDate
@@ -48,18 +44,6 @@ tableValues() ::= <<
>>
-updateVersion() ::= <<
-update <tableName()>
-set
-current_version = :currentVersion
-, updated_by = :createdBy
-, updated_date = :createdDate
-where
-id = :id
-<AND_CHECK_TENANT()>
-;
->>
-
unactiveEvent() ::= <<
update <tableName()>
set
@@ -72,17 +56,6 @@ id = :id
;
>>
-reactiveEvent() ::= <<
-update <tableName()>
-set
-is_active = true
-, updated_by = :createdBy
-, updated_date = :createdDate
-where
-event_id = :eventId
-<AND_CHECK_TENANT()>
-;
->>
diff --git a/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionSqlDao.sql.stg b/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionSqlDao.sql.stg
index 55b2cdb..f644d2d 100644
--- a/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionSqlDao.sql.stg
+++ b/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionSqlDao.sql.stg
@@ -7,8 +7,8 @@ tableFields(prefix) ::= <<
, <prefix>category
, <prefix>start_date
, <prefix>bundle_start_date
-, <prefix>active_version
, <prefix>charged_through_date
+, <prefix>migrated
, <prefix>created_by
, <prefix>created_date
, <prefix>updated_by
@@ -20,8 +20,8 @@ tableValues() ::= <<
, :category
, :startDate
, :bundleStartDate
-, :activeVersion
, :chargedThroughDate
+, :migrated
, :createdBy
, :createdDate
, :updatedBy
@@ -50,25 +50,3 @@ where id = :id
;
>>
-updateActiveVersion() ::= <<
-update <tableName()>
-set
-active_version = :activeVersion
-, updated_by = :createdBy
-, updated_date = :createdDate
-where id = :id
-;
->>
-
-updateForRepair() ::= <<
-update <tableName()>
-set
-active_version = :activeVersion
-, start_date = :startDate
-, bundle_start_date = :bundleStartDate
-, updated_by = :createdBy
-, updated_date = :createdDate
-where id = :id
-<AND_CHECK_TENANT()>
-;
->>
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/alignment/TestPlanAligner.java b/subscription/src/test/java/org/killbill/billing/subscription/alignment/TestPlanAligner.java
index 0ec542a..513ddad 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/alignment/TestPlanAligner.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/alignment/TestPlanAligner.java
@@ -52,20 +52,6 @@ public class TestPlanAligner extends SubscriptionTestSuiteNoDB {
@BeforeClass(groups = "fast")
public void beforeClass() throws Exception {
super.beforeClass();
-
- /*
- final VersionedCatalogLoader versionedCatalogLoader = new VersionedCatalogLoader(clock);
- final CatalogConfig config = new ConfigurationObjectFactory(new ConfigSource() {
- final Map<String, String> properties = ImmutableMap.<String, String>of("org.killbill.catalog.uri", "file:src/test/resources/testInput.xml");
-
- @Override
- public String getString(final String propertyName) {
- return properties.get(propertyName);
- }
- }).build(CatalogConfig.class);
-
- catalogService = new DefaultCatalogService(config, versionedCatalogLoader);
- */
planAligner = new PlanAligner(catalogService);
}
@@ -171,8 +157,8 @@ public class TestPlanAligner extends SubscriptionTestSuiteNoDB {
final SubscriptionBaseEvent event = createSubscriptionEvent(builder.getAlignStartDate(),
productName,
phaseType,
- ApiEventType.CREATE,
- defaultSubscriptionBase.getActiveVersion());
+ ApiEventType.CREATE
+ );
defaultSubscriptionBase.rebuildTransitions(ImmutableList.<SubscriptionBaseEvent>of(event), catalogService.getFullCatalog(internalCallContext));
Assert.assertEquals(defaultSubscriptionBase.getAllTransitions().size(), 1);
@@ -190,13 +176,13 @@ public class TestPlanAligner extends SubscriptionTestSuiteNoDB {
final SubscriptionBaseEvent previousEvent = createSubscriptionEvent(defaultSubscriptionBase.getStartDate(),
previousProductName,
commonPhaseType,
- ApiEventType.CREATE,
- defaultSubscriptionBase.getActiveVersion());
+ ApiEventType.CREATE
+ );
final SubscriptionBaseEvent event = createSubscriptionEvent(effectiveChangeDate,
newProductName,
commonPhaseType,
- ApiEventType.CHANGE,
- defaultSubscriptionBase.getActiveVersion());
+ ApiEventType.CHANGE
+ );
defaultSubscriptionBase.rebuildTransitions(ImmutableList.<SubscriptionBaseEvent>of(previousEvent, event), catalogService.getFullCatalog(internalCallContext));
@@ -210,8 +196,7 @@ public class TestPlanAligner extends SubscriptionTestSuiteNoDB {
private SubscriptionBaseEvent createSubscriptionEvent(final DateTime effectiveDate,
final String productName,
final PhaseType phaseType,
- final ApiEventType apiEventType,
- final long activeVersion) {
+ final ApiEventType apiEventType) {
final ApiEventBuilder eventBuilder = new ApiEventBuilder();
eventBuilder.setEffectiveDate(effectiveDate);
eventBuilder.setEventPlan(productName);
@@ -220,7 +205,6 @@ public class TestPlanAligner extends SubscriptionTestSuiteNoDB {
// We don't really use the following but the code path requires it
eventBuilder.setFromDisk(true);
- eventBuilder.setActiveVersion(activeVersion);
return new ApiEventBase(eventBuilder.setApiEventType(apiEventType));
}
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/TestEventJson.java b/subscription/src/test/java/org/killbill/billing/subscription/api/TestEventJson.java
index 90b3822..cee8006 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/TestEventJson.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/TestEventJson.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -19,16 +21,15 @@ package org.killbill.billing.subscription.api;
import java.util.UUID;
import org.joda.time.DateTime;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import org.killbill.billing.GuicyKillbillTestSuiteNoDB;
import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
-import org.killbill.billing.subscription.api.user.DefaultEffectiveSubscriptionEvent;
import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
+import org.killbill.billing.subscription.SubscriptionTestSuiteNoDB;
+import org.killbill.billing.subscription.api.user.DefaultEffectiveSubscriptionEvent;
import org.killbill.billing.util.jackson.ObjectMapper;
+import org.testng.Assert;
+import org.testng.annotations.Test;
-public class TestEventJson extends GuicyKillbillTestSuiteNoDB {
+public class TestEventJson extends SubscriptionTestSuiteNoDB {
private final ObjectMapper mapper = new ObjectMapper();
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java b/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java
index 6483634..2a377af 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -22,6 +22,7 @@ import java.util.List;
import java.util.UUID;
import org.joda.time.DateTime;
+import org.killbill.billing.account.api.AccountInternalApi;
import org.mockito.Mockito;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
@@ -64,10 +65,9 @@ public class TestDefaultSubscriptionTransferApi extends SubscriptionTestSuiteNoD
super.beforeMethod();
final NonEntityDao nonEntityDao = Mockito.mock(NonEntityDao.class);
final SubscriptionDao dao = Mockito.mock(SubscriptionDao.class);
- final CatalogService catalogService = new MockCatalogService(new MockCatalog(), cacheControllerDispatcher);
+ final CatalogService catalogService = new MockCatalogService(new MockCatalog(), cacheControllerDispatcher);
final SubscriptionBaseApiService apiService = Mockito.mock(SubscriptionBaseApiService.class);
final SubscriptionBaseTimelineApi timelineApi = Mockito.mock(SubscriptionBaseTimelineApi.class);
- final InternalCallContextFactory internalCallContextFactory = new InternalCallContextFactory(clock, nonEntityDao, new CacheControllerDispatcher());
transferApi = new DefaultSubscriptionBaseTransferApi(clock, dao, timelineApi, catalogService, apiService, internalCallContextFactory);
}
@@ -104,71 +104,6 @@ public class TestDefaultSubscriptionTransferApi extends SubscriptionTestSuiteNoD
Assert.assertEquals(((ApiEventTransfer) events.get(0)).getApiEventType(), ApiEventType.TRANSFER);
}
- @Test(groups = "fast")
- public void testEventsAfterTransferForMigratedBundle1() throws Exception {
- // MIGRATE_ENTITLEMENT then MIGRATE_BILLING (both in the past)
- final DateTime transferDate = clock.getUTCNow();
- final DateTime migrateSubscriptionEventEffectiveDate = transferDate.minusDays(10);
- final DateTime migrateBillingEventEffectiveDate = migrateSubscriptionEventEffectiveDate.plusDays(1);
- final List<SubscriptionBaseEvent> events = transferBundle(migrateSubscriptionEventEffectiveDate, migrateBillingEventEffectiveDate, transferDate);
-
- Assert.assertEquals(events.size(), 1);
- Assert.assertEquals(events.get(0).getType(), EventType.API_USER);
- Assert.assertEquals(events.get(0).getEffectiveDate(), transferDate);
- Assert.assertEquals(((ApiEventTransfer) events.get(0)).getApiEventType(), ApiEventType.TRANSFER);
- }
-
- @Test(groups = "fast")
- public void testEventsAfterTransferForMigratedBundle2() throws Exception {
- // MIGRATE_ENTITLEMENT and MIGRATE_BILLING at the same time (both in the past)
- final DateTime transferDate = clock.getUTCNow();
- final DateTime migrateSubscriptionEventEffectiveDate = transferDate.minusDays(10);
- final DateTime migrateBillingEventEffectiveDate = migrateSubscriptionEventEffectiveDate;
- final List<SubscriptionBaseEvent> events = transferBundle(migrateSubscriptionEventEffectiveDate, migrateBillingEventEffectiveDate, transferDate);
-
- Assert.assertEquals(events.size(), 1);
- Assert.assertEquals(events.get(0).getType(), EventType.API_USER);
- Assert.assertEquals(events.get(0).getEffectiveDate(), transferDate);
- Assert.assertEquals(((ApiEventTransfer) events.get(0)).getApiEventType(), ApiEventType.TRANSFER);
- }
-
- @Test(groups = "fast")
- public void testEventsAfterTransferForMigratedBundle3() throws Exception {
- // MIGRATE_ENTITLEMENT then MIGRATE_BILLING (the latter in the future)
- final DateTime transferDate = clock.getUTCNow();
- final DateTime migrateSubscriptionEventEffectiveDate = transferDate.minusDays(10);
- final DateTime migrateBillingEventEffectiveDate = migrateSubscriptionEventEffectiveDate.plusDays(20);
- final List<SubscriptionBaseEvent> events = transferBundle(migrateSubscriptionEventEffectiveDate, migrateBillingEventEffectiveDate, transferDate);
-
- Assert.assertEquals(events.size(), 1);
- Assert.assertEquals(events.get(0).getType(), EventType.API_USER);
- Assert.assertEquals(events.get(0).getEffectiveDate(), transferDate);
- Assert.assertEquals(((ApiEventTransfer) events.get(0)).getApiEventType(), ApiEventType.TRANSFER);
- }
-
- @Test(groups = "fast")
- public void testEventsAfterTransferForMigratedBundle4() throws Exception {
- // MIGRATE_ENTITLEMENT then MIGRATE_BILLING (both in the future)
- final DateTime transferDate = clock.getUTCNow();
- final DateTime migrateSubscriptionEventEffectiveDate = transferDate.plusDays(10);
- final DateTime migrateBillingEventEffectiveDate = migrateSubscriptionEventEffectiveDate.plusDays(20);
- final List<SubscriptionBaseEvent> events = transferBundle(migrateSubscriptionEventEffectiveDate, migrateBillingEventEffectiveDate, transferDate);
-
- Assert.assertEquals(events.size(), 1);
- Assert.assertEquals(events.get(0).getType(), EventType.API_USER);
- Assert.assertEquals(events.get(0).getEffectiveDate(), migrateSubscriptionEventEffectiveDate);
- Assert.assertEquals(((ApiEventTransfer) events.get(0)).getApiEventType(), ApiEventType.TRANSFER);
- }
-
- private List<SubscriptionBaseEvent> transferBundle(final DateTime migrateSubscriptionEventEffectiveDate, final DateTime migrateBillingEventEffectiveDate,
- final DateTime transferDate) throws SubscriptionBaseTransferApiException {
- final ImmutableList<ExistingEvent> existingEvents = createMigrateEvents(migrateSubscriptionEventEffectiveDate, migrateBillingEventEffectiveDate);
- final SubscriptionBuilder subscriptionBuilder = new SubscriptionBuilder();
- final DefaultSubscriptionBase subscription = new DefaultSubscriptionBase(subscriptionBuilder);
-
- return transferApi.toEvents(existingEvents, subscription, transferDate, internalCallContext);
- }
-
private ExistingEvent createEvent(final DateTime eventEffectiveDate, final SubscriptionBaseTransitionType subscriptionTransitionType) {
return new ExistingEvent() {
@Override
@@ -199,94 +134,10 @@ public class TestDefaultSubscriptionTransferApi extends SubscriptionTestSuiteNoD
}
@Override
- public DateTime getRequestedDate() {
- return getEffectiveDate();
- }
-
- @Override
public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
return subscriptionTransitionType;
}
};
}
- private ImmutableList<ExistingEvent> createMigrateEvents(final DateTime migrateSubscriptionEventEffectiveDate, final DateTime migrateBillingEventEffectiveDate) {
- final ExistingEvent migrateEntitlementEvent = new ExistingEvent() {
- @Override
- public DateTime getEffectiveDate() {
- return migrateSubscriptionEventEffectiveDate;
- }
-
- @Override
- public String getPlanName() {
- return "BicycleTrialEvergreen1USD";
- }
-
- @Override
- public String getPlanPhaseName() {
- return "BicycleTrialEvergreen1USD-trial";
- }
-
- @Override
- public UUID getEventId() {
- return UUID.randomUUID();
- }
-
- @Override
- public PlanPhaseSpecifier getPlanPhaseSpecifier() {
- return new PlanPhaseSpecifier("BicycleTrialEvergreen1USD", ProductCategory.BASE, BillingPeriod.NO_BILLING_PERIOD,
- PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.FIXEDTERM);
- }
-
- @Override
- public DateTime getRequestedDate() {
- return getEffectiveDate();
- }
-
- @Override
- public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
- return SubscriptionBaseTransitionType.MIGRATE_ENTITLEMENT;
- }
- };
-
- final ExistingEvent migrateBillingEvent = new ExistingEvent() {
-
- @Override
- public DateTime getEffectiveDate() {
- return migrateBillingEventEffectiveDate;
- }
-
- @Override
- public String getPlanName() {
- return migrateEntitlementEvent.getPlanName();
- }
-
- @Override
- public String getPlanPhaseName() {
- return migrateEntitlementEvent.getPlanPhaseName();
- }
-
- @Override
- public UUID getEventId() {
- return UUID.randomUUID();
- }
-
- @Override
- public PlanPhaseSpecifier getPlanPhaseSpecifier() {
- return migrateEntitlementEvent.getPlanPhaseSpecifier();
- }
-
- @Override
- public DateTime getRequestedDate() {
- return migrateEntitlementEvent.getRequestedDate();
- }
-
- @Override
- public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
- return SubscriptionBaseTransitionType.MIGRATE_BILLING;
- }
- };
-
- return ImmutableList.<ExistingEvent>of(migrateEntitlementEvent, migrateBillingEvent);
- }
}
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestTransfer.java b/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestTransfer.java
index 4751213..664d8cb 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestTransfer.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestTransfer.java
@@ -33,8 +33,6 @@ import org.killbill.billing.catalog.api.Product;
import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
import org.killbill.billing.subscription.SubscriptionTestSuiteWithEmbeddedDB;
import org.killbill.billing.subscription.api.SubscriptionBase;
-import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
-import org.killbill.billing.subscription.api.migration.SubscriptionBaseMigrationApi.AccountMigration;
import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
import org.slf4j.Logger;
@@ -61,64 +59,17 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
// Note: this will cleanup all tables
super.beforeMethod();
- final AccountData accountData = subscriptionTestInitializer.initAccountData();
- final Account account = accountUserApi.createAccount(accountData, callContext);
- newAccountId = account.getId();
final AccountData accountData2 = subscriptionTestInitializer.initAccountData();
- final Account account2 = accountUserApi.createAccount(accountData2, callContext);
+ final Account account2 = createAccount(accountData2);
finalNewAccountId = account2.getId();
- }
-
- @Test(groups = "slow")
- public void testTransferMigratedSubscriptionWithCTDInFuture() throws Exception {
- final DateTime startDate = clock.getUTCNow().minusMonths(2);
- final DateTime beforeMigration = clock.getUTCNow();
- final AccountMigration toBeMigrated = testUtil.createAccountForMigrationWithRegularBasePlan(startDate);
- final DateTime afterMigration = clock.getUTCNow();
-
- testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
- migrationApi.migrate(toBeMigrated, callContext);
- assertListenerStatus();
- final List<SubscriptionBaseBundle> bundles = subscriptionInternalApi.getBundlesForAccount(toBeMigrated.getAccountKey(), internalCallContext);
- assertEquals(bundles.size(), 1);
- final SubscriptionBaseBundle bundle = bundles.get(0);
-
- final DateTime bundleCreatedDate = bundle.getCreatedDate();
-
- final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(bundle.getId(), null, internalCallContext);
- assertEquals(subscriptions.size(), 1);
- final SubscriptionBase subscription = subscriptions.get(0);
- testUtil.assertDateWithin(subscription.getStartDate(), beforeMigration.minusMonths(2), afterMigration.minusMonths(2));
- assertEquals(subscription.getEndDate(), null);
- assertEquals(subscription.getCurrentPriceList().getName(), PriceListSet.DEFAULT_PRICELIST_NAME);
- assertEquals(subscription.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
- assertEquals(subscription.getState(), EntitlementState.ACTIVE);
- assertEquals(subscription.getCurrentPlan().getName(), "shotgun-annual");
- assertEquals(subscription.getChargedThroughDate(), startDate.plusYears(1));
- // WE should see MIGRATE_ENTITLEMENT and then MIGRATE_BILLING in the future
- assertEquals(subscriptionInternalApi.getBillingTransitions(subscription, internalCallContext).size(), 1);
- assertEquals(subscriptionInternalApi.getBillingTransitions(subscription, internalCallContext).get(0).getTransitionType(), SubscriptionBaseTransitionType.MIGRATE_BILLING);
- assertTrue(subscriptionInternalApi.getBillingTransitions(subscription, internalCallContext).get(0).getEffectiveTransitionTime().compareTo(clock.getUTCNow()) > 0);
- assertListenerStatus();
-
- // MOVE A LITTLE, STILL IN TRIAL
- clock.addDays(20);
-
- final DateTime transferRequestedDate = clock.getUTCNow();
-
- testListener.pushExpectedEvent(NextEvent.TRANSFER);
- testListener.pushExpectedEvent(NextEvent.CANCEL);
- transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getExternalKey(), transferRequestedDate, false, true, callContext);
- assertListenerStatus();
-
- final SubscriptionBase oldBaseSubscription = subscriptionInternalApi.getBaseSubscription(bundle.getId(), internalCallContext);
- assertTrue(oldBaseSubscription.getState() == EntitlementState.CANCELLED);
- // The MIGRATE_BILLING event should have been invalidated
- assertEquals(subscriptionInternalApi.getBillingTransitions(oldBaseSubscription, internalCallContext).size(), 0);
- //assertEquals(subscriptionInternalApi.getBillingTransitions(oldBaseSubscription, internalCallContext).get(0).getTransitionType(), SubscriptionBaseTransitionType.CANCEL);
+ // internal context will be configured for newAccountId
+ final AccountData accountData = subscriptionTestInitializer.initAccountData();
+ final Account account = createAccount(accountData);
+ newAccountId = account.getId();
}
+
@Test(groups = "slow")
public void testTransferBPInTrialWithNoCTD() throws Exception {
final String baseProduct = "Shotgun";
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java
index 487a3d9..d93ee18 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java
@@ -19,7 +19,6 @@
package org.killbill.billing.subscription.api.user;
import java.util.ArrayList;
-import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
@@ -27,9 +26,8 @@ import javax.annotation.Nullable;
import javax.inject.Inject;
import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
import org.joda.time.Period;
-import org.killbill.billing.account.api.Account;
-import org.killbill.billing.account.api.AccountApiException;
import org.killbill.billing.account.api.AccountUserApi;
import org.killbill.billing.api.TestApiListener;
import org.killbill.billing.api.TestApiListener.NextEvent;
@@ -38,16 +36,9 @@ import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Duration;
import org.killbill.billing.catalog.api.PhaseType;
import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
-import org.killbill.billing.catalog.api.PriceListSet;
import org.killbill.billing.catalog.api.ProductCategory;
import org.killbill.billing.catalog.api.TimeUnit;
-import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
-import org.killbill.billing.mock.MockAccountBuilder;
import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
-import org.killbill.billing.subscription.api.migration.SubscriptionBaseMigrationApi.AccountMigration;
-import org.killbill.billing.subscription.api.migration.SubscriptionBaseMigrationApi.BundleMigration;
-import org.killbill.billing.subscription.api.migration.SubscriptionBaseMigrationApi.SubscriptionMigration;
-import org.killbill.billing.subscription.api.migration.SubscriptionBaseMigrationApi.SubscriptionMigrationCase;
import org.killbill.billing.subscription.engine.dao.SubscriptionDao;
import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
import org.killbill.billing.subscription.events.phase.PhaseEvent;
@@ -101,7 +92,7 @@ public class TestSubscriptionHelper {
testListener.pushExpectedEvent(NextEvent.CREATE);
final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionApi.createSubscription(bundleId,
new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSet, null), null,
- requestedDate == null ? clock.getUTCNow() : requestedDate, internalCallContext);
+ requestedDate == null ? clock.getUTCNow() : requestedDate, false, internalCallContext);
assertNotNull(subscription);
testListener.assertListenerStatus();
@@ -154,9 +145,12 @@ public class TestSubscriptionHelper {
@Override
public DateTime addToDateTime(final DateTime dateTime) {
- return null; //To change body of implemented methods use File | Settings | File Templates.
+ return null;
+ }
+ @Override
+ public LocalDate addToLocalDate(final LocalDate localDate) {
+ return null;
}
-
@Override
public Period toJodaPeriod() {
throw new UnsupportedOperationException();
@@ -177,169 +171,6 @@ public class TestSubscriptionHelper {
}
}
- public void printSubscriptionTransitions(final List<EffectiveSubscriptionInternalEvent> transitions) {
- for (final EffectiveSubscriptionInternalEvent cur : transitions) {
- log.debug("Transition " + cur);
- }
- }
-
- /**
- * ***********************************************************
- * Utilities for migration tests
- * *************************************************************
- */
-
- public AccountMigration createAccountForMigrationTest(final List<List<SubscriptionMigrationCaseWithCTD>> cases) {
- final Account account;
- try {
- final Account accountData = new MockAccountBuilder().name(UUID.randomUUID().toString().substring(1, 8))
- .email(UUID.randomUUID().toString().substring(1, 8))
- .build();
- account = accountApi.createAccount(accountData, callContext);
- } catch (final AccountApiException e) {
- throw new AssertionError(e.getLocalizedMessage());
- }
-
- return new AccountMigration() {
-
- @Override
- public BundleMigration[] getBundles() {
- final List<BundleMigration> bundles = new ArrayList<BundleMigration>();
- final BundleMigration bundle0 = new BundleMigration() {
- @Override
- public SubscriptionMigration[] getSubscriptions() {
- final SubscriptionMigration[] result = new SubscriptionMigration[cases.size()];
-
- for (int i = 0; i < cases.size(); i++) {
- final List<SubscriptionMigrationCaseWithCTD> curCases = cases.get(i);
- final SubscriptionMigration subscription = new SubscriptionMigration() {
- @Override
- public SubscriptionMigrationCaseWithCTD[] getSubscriptionCases() {
- return curCases.toArray(new SubscriptionMigrationCaseWithCTD[curCases.size()]);
- }
-
- @Override
- public ProductCategory getCategory() {
- return curCases.get(0).getPlanPhaseSpecifier().getProductCategory();
- }
-
- @Override
- public DateTime getChargedThroughDate() {
- for (final SubscriptionMigrationCaseWithCTD cur : curCases) {
- if (cur.getChargedThroughDate() != null) {
- return cur.getChargedThroughDate();
- }
- }
- return null;
- }
- };
- result[i] = subscription;
- }
- return result;
- }
-
- @Override
- public String getBundleKey() {
- return "12345";
- }
- };
- bundles.add(bundle0);
- return bundles.toArray(new BundleMigration[bundles.size()]);
- }
-
- @Override
- public UUID getAccountKey() {
- return account.getId();
- }
- };
- }
-
- public AccountMigration createAccountForMigrationWithRegularBasePlanAndAddons(final DateTime initialBPstart, final DateTime initalAddonStart) {
-
- final List<SubscriptionMigrationCaseWithCTD> cases = new LinkedList<SubscriptionMigrationCaseWithCTD>();
- cases.add(new SubscriptionMigrationCaseWithCTD(
- new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
- initialBPstart,
- null,
- initialBPstart.plusYears(1)));
-
- final List<SubscriptionMigrationCaseWithCTD> firstAddOnCases = new LinkedList<SubscriptionMigrationCaseWithCTD>();
- firstAddOnCases.add(new SubscriptionMigrationCaseWithCTD(
- new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.DISCOUNT),
- initalAddonStart,
- initalAddonStart.plusMonths(1),
- initalAddonStart.plusMonths(1)));
- firstAddOnCases.add(new SubscriptionMigrationCaseWithCTD(
- new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
- initalAddonStart.plusMonths(1),
- null,
- null));
-
- final List<List<SubscriptionMigrationCaseWithCTD>> input = new ArrayList<List<SubscriptionMigrationCaseWithCTD>>();
- input.add(cases);
- input.add(firstAddOnCases);
- return createAccountForMigrationTest(input);
- }
-
- public AccountMigration createAccountForMigrationWithRegularBasePlan(final DateTime startDate) {
- final List<SubscriptionMigrationCaseWithCTD> cases = new LinkedList<SubscriptionMigrationCaseWithCTD>();
- cases.add(new SubscriptionMigrationCaseWithCTD(
- new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
- startDate,
- null,
- startDate.plusYears(1)));
- final List<List<SubscriptionMigrationCaseWithCTD>> input = new ArrayList<List<SubscriptionMigrationCaseWithCTD>>();
- input.add(cases);
- return createAccountForMigrationTest(input);
- }
-
- public AccountMigration createAccountForMigrationWithRegularBasePlanFutreCancelled(final DateTime startDate) {
- final List<SubscriptionMigrationCaseWithCTD> cases = new LinkedList<SubscriptionMigrationCaseWithCTD>();
- cases.add(new SubscriptionMigrationCaseWithCTD(
- new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
- startDate,
- startDate.plusYears(1),
- startDate.plusYears(1)));
- final List<List<SubscriptionMigrationCaseWithCTD>> input = new ArrayList<List<SubscriptionMigrationCaseWithCTD>>();
- input.add(cases);
- return createAccountForMigrationTest(input);
- }
-
- public AccountMigration createAccountForMigrationFuturePendingPhase(final DateTime trialDate) {
- final List<SubscriptionMigrationCaseWithCTD> cases = new LinkedList<SubscriptionMigrationCaseWithCTD>();
- cases.add(new SubscriptionMigrationCaseWithCTD(
- new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.TRIAL),
- trialDate,
- trialDate.plusDays(30),
- trialDate.plusDays(30)));
- cases.add(new SubscriptionMigrationCaseWithCTD(
- new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
- trialDate.plusDays(30),
- null,
- null));
- final List<List<SubscriptionMigrationCaseWithCTD>> input = new ArrayList<List<SubscriptionMigrationCaseWithCTD>>();
- input.add(cases);
- return createAccountForMigrationTest(input);
- }
-
- public AccountMigration createAccountForMigrationFuturePendingChange() {
- final List<SubscriptionMigrationCaseWithCTD> cases = new LinkedList<SubscriptionMigrationCaseWithCTD>();
- final DateTime effectiveDate = clock.getUTCNow().minusDays(10);
- cases.add(new SubscriptionMigrationCaseWithCTD(
- new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
- effectiveDate,
- effectiveDate.plusMonths(1),
- effectiveDate.plusMonths(1)));
- cases.add(new SubscriptionMigrationCaseWithCTD(
- new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
- effectiveDate.plusMonths(1).plusDays(1),
- null,
- null));
- final List<List<SubscriptionMigrationCaseWithCTD>> input = new ArrayList<List<SubscriptionMigrationCaseWithCTD>>();
- input.add(cases);
- return createAccountForMigrationTest(input);
- }
-
public static DateTime addOrRemoveDuration(final DateTime input, final List<Duration> durations, final boolean add) {
DateTime result = input;
for (final Duration cur : durations) {
@@ -373,38 +204,4 @@ public class TestSubscriptionHelper {
return addOrRemoveDuration(input, list, true);
}
- public static class SubscriptionMigrationCaseWithCTD implements SubscriptionMigrationCase {
-
- private final PlanPhaseSpecifier pps;
- private final DateTime effDt;
- private final DateTime cancelDt;
- private final DateTime ctd;
-
- public SubscriptionMigrationCaseWithCTD(final PlanPhaseSpecifier pps, final DateTime effDt, final DateTime cancelDt, final DateTime ctd) {
- this.pps = pps;
- this.cancelDt = cancelDt;
- this.effDt = effDt;
- this.ctd = ctd;
- }
-
- @Override
- public PlanPhaseSpecifier getPlanPhaseSpecifier() {
- return pps;
- }
-
- @Override
- public DateTime getEffectiveDate() {
- return effDt;
- }
-
- @Override
- public DateTime getCancelledDate() {
- return cancelDt;
- }
-
- public DateTime getChargedThroughDate() {
- return ctd;
- }
- }
-
}
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCreate.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCreate.java
index d85336b..4776379 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCreate.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCreate.java
@@ -27,6 +27,7 @@ import org.killbill.billing.catalog.api.Plan;
import org.killbill.billing.catalog.api.PlanPhase;
import org.killbill.billing.catalog.api.PriceListSet;
import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
import org.killbill.billing.subscription.DefaultSubscriptionTestInitializer;
import org.killbill.billing.subscription.SubscriptionTestSuiteWithEmbeddedDB;
import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
@@ -57,7 +58,7 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.PHASE);
final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle.getId(),
- testUtil.getProductSpecifier(productName, planSetName, term, null), null, requestedDate, internalCallContext);
+ testUtil.getProductSpecifier(productName, planSetName, term, null), null, requestedDate, false, internalCallContext);
assertListenerStatus();
assertNotNull(subscription);
@@ -71,7 +72,7 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.PHASE);
final DefaultSubscriptionBase newSubscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(newBundle.getId(),
- testUtil.getProductSpecifier(productName, planSetName, term, null), null, requestedDate, internalCallContext);
+ testUtil.getProductSpecifier(productName, planSetName, term, null), null, requestedDate, false, internalCallContext);
subscriptionInternalApi.updateExternalKey(newBundle.getId(), "myNewSuperKey", internalCallContext);
@@ -95,7 +96,7 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
testListener.pushExpectedEvent(NextEvent.CREATE);
final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle.getId(),
- testUtil.getProductSpecifier(productName, planSetName, term, null), null, requestedDate, internalCallContext);
+ testUtil.getProductSpecifier(productName, planSetName, term, null), null, requestedDate, false, internalCallContext);
assertNotNull(subscription);
//
@@ -108,8 +109,6 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
final SubscriptionBaseEvent trialEvent = events.get(0);
final SubscriptionBaseEvent phaseEvent = events.get(1);
- assertEquals(subscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
- //assertEquals(subscription.getAccount(), account.getId());
assertEquals(subscription.getBundleId(), bundle.getId());
assertEquals(subscription.getStartDate(), requestedDate);
@@ -135,11 +134,9 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
testListener.pushExpectedEvent(NextEvent.CREATE);
final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle.getId(),
- testUtil.getProductSpecifier(productName, planSetName, term, PhaseType.EVERGREEN), null, clock.getUTCNow(), internalCallContext);
+ testUtil.getProductSpecifier(productName, planSetName, term, PhaseType.EVERGREEN), null, clock.getUTCNow(), false, internalCallContext);
assertNotNull(subscription);
- assertEquals(subscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
- //assertEquals(subscription.getAccount(), account.getId());
assertEquals(subscription.getBundleId(), bundle.getId());
testUtil.assertDateWithin(subscription.getStartDate(), init, clock.getUTCNow());
testUtil.assertDateWithin(subscription.getBundleStartDate(), init, clock.getUTCNow());
@@ -169,11 +166,9 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle.getId(),
testUtil.getProductSpecifier(productName, planSetName, term, null),
- null, clock.getUTCNow(), internalCallContext);
+ null, clock.getUTCNow(), false, internalCallContext);
assertNotNull(subscription);
- assertEquals(subscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
- //assertEquals(subscription.getAccount(), account.getId());
assertEquals(subscription.getBundleId(), bundle.getId());
testUtil.assertDateWithin(subscription.getStartDate(), init, clock.getUTCNow());
testUtil.assertDateWithin(subscription.getBundleStartDate(), init, clock.getUTCNow());
@@ -219,7 +214,7 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
// CREATE SUBSCRIPTION
DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle.getId(),
testUtil.getProductSpecifier(productName, planSetName, term, null),
- null, clock.getUTCNow(), internalCallContext);
+ null, clock.getUTCNow(), false, internalCallContext);
assertNotNull(subscription);
PlanPhase currentPhase = subscription.getCurrentPhase();
@@ -260,9 +255,35 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle.getId(),
testUtil.getProductSpecifier(productName, planSetName, term, null),
- null, clock.getUTCNow(), internalCallContext);
+ null, clock.getUTCNow(), false, internalCallContext);
assertNotNull(subscription);
assertListenerStatus();
}
+
+
+ @Test(groups = "slow")
+ public void testCreateSubscriptionInTheFuture() throws SubscriptionBaseApiException {
+ final DateTime init = clock.getUTCNow();
+
+ final String productName = "Shotgun";
+ final BillingPeriod term = BillingPeriod.MONTHLY;
+ final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+
+ final DateTime futureCreationDate = init.plusDays(10);
+
+ DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle.getId(),
+ testUtil.getProductSpecifier(productName, planSetName, term, null), null, futureCreationDate, false, internalCallContext);
+ assertListenerStatus();
+ assertNotNull(subscription);
+ assertEquals(subscription.getState(), EntitlementState.PENDING);
+
+ testListener.pushExpectedEvent(NextEvent.CREATE);
+ clock.addDays(10);
+ assertListenerStatus();
+
+ subscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
+ assertEquals(subscription.getState(), EntitlementState.ACTIVE);
+ }
}
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java
index 2166d4c..46f3ab6 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java
@@ -92,7 +92,7 @@ public class TestUserApiError extends SubscriptionTestSuiteNoDB {
try {
subscriptionInternalApi.createSubscription(bundleId,
testUtil.getProductSpecifier(productName, planSet, term, null),
- null, clock.getUTCNow(), internalCallContext);
+ null, clock.getUTCNow(), false, internalCallContext);
Assert.fail("Exception expected, error code: " + expected);
} catch (final SubscriptionBaseApiException e) {
assertEquals(e.getCode(), expected.getCode());
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java b/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
index f7947f5..0dfe039 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
@@ -38,9 +38,7 @@ import org.killbill.billing.catalog.api.TimeUnit;
import org.killbill.billing.dao.MockNonEntityDao;
import org.killbill.billing.entitlement.api.SubscriptionApiException;
import org.killbill.billing.subscription.api.SubscriptionBase;
-import org.killbill.billing.subscription.api.migration.AccountMigrationData;
-import org.killbill.billing.subscription.api.migration.AccountMigrationData.BundleMigrationData;
-import org.killbill.billing.subscription.api.migration.AccountMigrationData.SubscriptionMigrationData;
+import org.killbill.billing.subscription.api.transfer.BundleTransferData;
import org.killbill.billing.subscription.api.transfer.TransferCancelData;
import org.killbill.billing.subscription.api.user.DefaultEffectiveSubscriptionEvent;
import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
@@ -241,16 +239,6 @@ public class MockSubscriptionDaoMemory extends MockEntityDaoBase<SubscriptionBun
}
@Override
- public void recreateSubscription(final DefaultSubscriptionBase subscription, final List<SubscriptionBaseEvent> recreateEvents, final InternalCallContext context) {
- synchronized (events) {
- events.addAll(recreateEvents);
- for (final SubscriptionBaseEvent cur : recreateEvents) {
- recordFutureNotificationFromTransaction(null, cur.getEffectiveDate(), new SubscriptionNotificationKey(cur.getId()), context);
- }
- }
- }
-
- @Override
public List<SubscriptionBase> getSubscriptions(final UUID bundleId, final List<SubscriptionBaseEvent> dryRunEvents, final InternalTenantContext context) {
final List<SubscriptionBase> results = new ArrayList<SubscriptionBase>();
for (final SubscriptionBase cur : subscriptions) {
@@ -460,30 +448,6 @@ public class MockSubscriptionDaoMemory extends MockEntityDaoBase<SubscriptionBun
}
@Override
- public void migrate(final UUID accountId, final AccountMigrationData accountData, final InternalCallContext context) {
- synchronized (events) {
-
- for (final BundleMigrationData curBundle : accountData.getData()) {
- final DefaultSubscriptionBaseBundle bundleData = curBundle.getData();
- for (final SubscriptionMigrationData curSubscription : curBundle.getSubscriptions()) {
- final DefaultSubscriptionBase subData = curSubscription.getData();
- for (final SubscriptionBaseEvent curEvent : curSubscription.getInitialEvents()) {
- events.add(curEvent);
- mockNonEntityDao.addTenantRecordIdMapping(curEvent.getId(), context);
- recordFutureNotificationFromTransaction(null, curEvent.getEffectiveDate(),
- new SubscriptionNotificationKey(curEvent.getId()), context);
-
- }
- subscriptions.add(subData);
- mockNonEntityDao.addTenantRecordIdMapping(subData.getId(), context);
- }
- bundles.add(bundleData);
- mockNonEntityDao.addTenantRecordIdMapping(bundleData.getId(), context);
- }
- }
- }
-
- @Override
public SubscriptionBaseEvent getEventById(final UUID eventId, final InternalTenantContext context) {
synchronized (events) {
for (final SubscriptionBaseEvent cur : events) {
@@ -529,7 +493,7 @@ public class MockSubscriptionDaoMemory extends MockEntityDaoBase<SubscriptionBun
}
@Override
- public void transfer(final UUID srcAccountId, final UUID destAccountId, final BundleMigrationData data,
+ public void transfer(final UUID srcAccountId, final UUID destAccountId, final BundleTransferData data,
final List<TransferCancelData> transferCancelData, final InternalCallContext fromContext,
final InternalCallContext toContext) {
}
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoSql.java b/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoSql.java
index 4dd3357..2261e0b 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoSql.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoSql.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -16,6 +18,7 @@
package org.killbill.billing.subscription.engine.dao;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.skife.jdbi.v2.IDBI;
import org.killbill.bus.api.PersistentBus;
@@ -32,7 +35,8 @@ public class MockSubscriptionDaoSql extends DefaultSubscriptionDao {
@Inject
public MockSubscriptionDaoSql(final IDBI dbi, final Clock clock, final AddonUtils addonUtils, final NotificationQueueService notificationQueueService,
- final PersistentBus eventBus, final CatalogService catalogService, final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao) {
- super(dbi, clock, addonUtils, notificationQueueService, eventBus, catalogService, cacheControllerDispatcher, nonEntityDao);
+ final PersistentBus eventBus, final CatalogService catalogService, final CacheControllerDispatcher cacheControllerDispatcher,
+ final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory) {
+ super(dbi, clock, addonUtils, notificationQueueService, eventBus, catalogService, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory);
}
}
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/glue/TestDefaultSubscriptionModuleNoDB.java b/subscription/src/test/java/org/killbill/billing/subscription/glue/TestDefaultSubscriptionModuleNoDB.java
index 1bb0955..571f6ff 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/glue/TestDefaultSubscriptionModuleNoDB.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/glue/TestDefaultSubscriptionModuleNoDB.java
@@ -20,6 +20,7 @@ package org.killbill.billing.subscription.glue;
import org.killbill.billing.GuicyKillbillTestNoDBModule;
import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.mock.glue.MockAccountModule;
import org.killbill.billing.mock.glue.MockNonEntityDaoModule;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.subscription.engine.dao.MockSubscriptionDaoMemory;
@@ -40,8 +41,7 @@ public class TestDefaultSubscriptionModuleNoDB extends TestDefaultSubscriptionMo
@Override
protected void configure() {
install(new GuicyKillbillTestNoDBModule(configSource));
-
- bind(AccountUserApi.class).toInstance(Mockito.mock(AccountUserApi.class));
+ install(new MockAccountModule(configSource));
super.configure();
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java
index 9b74d30..0148bc4 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -32,7 +32,6 @@ import org.killbill.billing.lifecycle.api.BusService;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
import org.killbill.billing.subscription.api.SubscriptionBaseService;
-import org.killbill.billing.subscription.api.migration.SubscriptionBaseMigrationApi;
import org.killbill.billing.subscription.api.timeline.SubscriptionBaseTimelineApi;
import org.killbill.billing.subscription.api.transfer.SubscriptionBaseTransferApi;
import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
@@ -41,6 +40,7 @@ import org.killbill.billing.subscription.engine.dao.MockSubscriptionDaoMemory;
import org.killbill.billing.subscription.engine.dao.SubscriptionDao;
import org.killbill.billing.subscription.glue.TestDefaultSubscriptionModuleNoDB;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.config.SubscriptionConfig;
import org.killbill.clock.ClockMock;
import org.mockito.Mockito;
@@ -69,8 +69,6 @@ public class SubscriptionTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
protected SubscriptionBaseTransferApi transferApi;
@Inject
- protected SubscriptionBaseMigrationApi migrationApi;
- @Inject
protected SubscriptionBaseTimelineApi repairApi;
@Inject
@@ -101,6 +99,9 @@ public class SubscriptionTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@Inject
protected MockNonEntityDao mockNonEntityDao;
+ @Inject
+ protected InternalCallContextFactory internalCallContextFactory;
+
protected Catalog catalog;
protected AccountData accountData;
protected SubscriptionBaseBundle bundle;
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java
index e335d8f..59b5209 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java
@@ -22,6 +22,7 @@ import javax.inject.Inject;
import org.killbill.billing.GuicyKillbillTestSuiteWithEmbeddedDB;
import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountApiException;
import org.killbill.billing.account.api.AccountData;
import org.killbill.billing.account.api.AccountUserApi;
import org.killbill.billing.api.TestApiListener;
@@ -31,7 +32,6 @@ import org.killbill.billing.lifecycle.api.BusService;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
import org.killbill.billing.subscription.api.SubscriptionBaseService;
-import org.killbill.billing.subscription.api.migration.SubscriptionBaseMigrationApi;
import org.killbill.billing.subscription.api.timeline.SubscriptionBaseTimelineApi;
import org.killbill.billing.subscription.api.transfer.SubscriptionBaseTransferApi;
import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
@@ -39,6 +39,7 @@ import org.killbill.billing.subscription.api.user.TestSubscriptionHelper;
import org.killbill.billing.subscription.engine.dao.SubscriptionDao;
import org.killbill.billing.subscription.glue.TestDefaultSubscriptionModuleWithEmbeddedDB;
import org.killbill.billing.util.config.SubscriptionConfig;
+import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.clock.ClockMock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -66,8 +67,6 @@ public class SubscriptionTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteW
protected SubscriptionBaseTransferApi transferApi;
@Inject
- protected SubscriptionBaseMigrationApi migrationApi;
- @Inject
protected SubscriptionBaseTimelineApi repairApi;
@Inject
@@ -88,6 +87,9 @@ public class SubscriptionTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteW
@Inject
protected SubscriptionTestInitializer subscriptionTestInitializer;
+ @Inject
+ protected NonEntityDao nonEntityDao;
+
protected Catalog catalog;
protected AccountData accountData;
protected SubscriptionBaseBundle bundle;
@@ -111,7 +113,7 @@ public class SubscriptionTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteW
this.catalog = subscriptionTestInitializer.initCatalog(catalogService, internalCallContext);
this.accountData = subscriptionTestInitializer.initAccountData();
- final Account account = accountUserApi.createAccount(accountData, callContext);
+ final Account account = createAccount(accountData);
this.bundle = subscriptionTestInitializer.initBundle(account.getId(), subscriptionInternalApi, internalCallContext);
// Make sure we start with a clean state
@@ -126,6 +128,14 @@ public class SubscriptionTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteW
subscriptionTestInitializer.stopTestFramework(testListener, busService, subscriptionBaseService);
}
+ protected Account createAccount(final AccountData accountData) throws AccountApiException {
+ final Account account = accountUserApi.createAccount(accountData, callContext);
+
+ refreshCallContext(account.getId());
+
+ return account;
+ }
+
protected void assertListenerStatus() {
testListener.assertListenerStatus();
}
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java b/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java
index 02ac812..a49f041 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -178,7 +178,7 @@ public class TenantCacheInvalidation {
if (tenantKeyAndCookie != null) {
final CacheInvalidationCallback callback = parent.getCacheInvalidation(tenantKeyAndCookie.getTenantKey());
if (callback != null) {
- final InternalTenantContext tenantContext = new InternalTenantContext(cur.getTenantRecordId(), null);
+ final InternalTenantContext tenantContext = new InternalTenantContext(cur.getTenantRecordId());
callback.invalidateCache(tenantKeyAndCookie.getTenantKey(), tenantKeyAndCookie.getCookie(), tenantContext);
final Long tenantKvsTargetRecordId = cur.getTargetRecordId();
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/api/user/DefaultTenantUserApi.java b/tenant/src/main/java/org/killbill/billing/tenant/api/user/DefaultTenantUserApi.java
index 3c1c575..be914f5 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/api/user/DefaultTenantUserApi.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/api/user/DefaultTenantUserApi.java
@@ -83,7 +83,7 @@ public class DefaultTenantUserApi implements TenantUserApi {
final Tenant tenant = new DefaultTenant(data);
try {
- tenantDao.create(new TenantModelDao(tenant), internalCallContextFactory.createInternalCallContext(context));
+ tenantDao.create(new TenantModelDao(tenant), internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(context));
} catch (final TenantApiException e) {
throw new TenantApiException(e, ErrorCode.TENANT_CREATION_FAILED);
}
@@ -102,7 +102,7 @@ public class DefaultTenantUserApi implements TenantUserApi {
@Override
public Tenant getTenantById(final UUID id) throws TenantApiException {
// TODO - API cleanup?
- final TenantModelDao tenant = tenantDao.getById(id, new InternalTenantContext(null, null));
+ final TenantModelDao tenant = tenantDao.getById(id, new InternalTenantContext(null));
if (tenant == null) {
throw new TenantApiException(ErrorCode.TENANT_DOES_NOT_EXIST_FOR_ID, id);
}
@@ -111,7 +111,7 @@ public class DefaultTenantUserApi implements TenantUserApi {
@Override
public List<String> getTenantValuesForKey(final String key, final TenantContext context) throws TenantApiException {
- final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
+ final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context);
if (!isCachedInTenantKVCache(key)) {
return tenantDao.getTenantValueForKey(key, internalContext);
} else {
@@ -122,7 +122,7 @@ public class DefaultTenantUserApi implements TenantUserApi {
@Override
public void addTenantKeyValue(final String key, final String value, final CallContext context) throws TenantApiException {
// Invalidate tenantKVCache after we store (to avoid race conditions). Multi-node invalidation will follow the TenantBroadcast pattern
- final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(context);
+ final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(context);
final String tenantKey = getCacheKeyName(key, internalContext);
tenantDao.addTenantKeyValue(key, value, isSingleValueKey(key), internalContext);
tenantKVCache.remove(tenantKey);
@@ -131,7 +131,7 @@ public class DefaultTenantUserApi implements TenantUserApi {
@Override
public void deleteTenantKey(final String key, final CallContext context) throws TenantApiException {
// Invalidate tenantKVCache after we delete (to avoid race conditions). Multi-node invalidation will follow the TenantBroadcast pattern
- final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(context);
+ final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(context);
final String tenantKey = getCacheKeyName(key, internalContext);
tenantDao.deleteTenantKey(key, internalContext);
tenantKVCache.remove(tenantKey);
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/dao/DefaultTenantBroadcastDao.java b/tenant/src/main/java/org/killbill/billing/tenant/dao/DefaultTenantBroadcastDao.java
index 0ac2964..5fa4996 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/dao/DefaultTenantBroadcastDao.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/dao/DefaultTenantBroadcastDao.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -24,6 +24,7 @@ import javax.inject.Inject;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.tenant.api.TenantApiException;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.entity.Entity;
import org.killbill.billing.util.entity.dao.EntityDaoBase;
@@ -31,12 +32,12 @@ import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper
import org.killbill.clock.Clock;
import org.skife.jdbi.v2.IDBI;
-
public class DefaultTenantBroadcastDao extends EntityDaoBase<TenantBroadcastModelDao, Entity, TenantApiException> implements TenantBroadcastDao {
@Inject
- public DefaultTenantBroadcastDao(final IDBI dbi, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao) {
- super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao), TenantBroadcastSqlDao.class);
+ public DefaultTenantBroadcastDao(final IDBI dbi, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher,
+ final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory) {
+ super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory), TenantBroadcastSqlDao.class);
}
@Override
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/dao/DefaultTenantDao.java b/tenant/src/main/java/org/killbill/billing/tenant/dao/DefaultTenantDao.java
index 4d929d4..1e12715 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/dao/DefaultTenantDao.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/dao/DefaultTenantDao.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -37,6 +37,7 @@ import org.killbill.billing.tenant.api.TenantApiException;
import org.killbill.billing.tenant.api.TenantKV.TenantKey;
import org.killbill.billing.util.UUIDs;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.config.SecurityConfig;
import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.entity.dao.EntityDaoBase;
@@ -62,8 +63,9 @@ public class DefaultTenantDao extends EntityDaoBase<TenantModelDao, Tenant, Tena
private final SecurityConfig securityConfig;
@Inject
- public DefaultTenantDao(final IDBI dbi, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao, final SecurityConfig securityConfig) {
- super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao), TenantSqlDao.class);
+ public DefaultTenantDao(final IDBI dbi, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher,
+ final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory, final SecurityConfig securityConfig) {
+ super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory), TenantSqlDao.class);
this.securityConfig = securityConfig;
}
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/dao/NoCachingTenantBroadcastDao.java b/tenant/src/main/java/org/killbill/billing/tenant/dao/NoCachingTenantBroadcastDao.java
index 750a60f..eef661e 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/dao/NoCachingTenantBroadcastDao.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/dao/NoCachingTenantBroadcastDao.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -21,10 +21,13 @@ import java.util.List;
import java.util.UUID;
import javax.inject.Inject;
+import javax.inject.Named;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.tenant.api.TenantApiException;
+import org.killbill.billing.tenant.glue.DefaultTenantModule;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.entity.Entity;
import org.killbill.billing.util.entity.Pagination;
import org.killbill.billing.util.entity.dao.EntityDaoBase;
@@ -37,8 +40,8 @@ import org.skife.jdbi.v2.IDBI;
public class NoCachingTenantBroadcastDao extends EntityDaoBase<TenantBroadcastModelDao, Entity, TenantApiException> implements TenantBroadcastDao {
@Inject
- public NoCachingTenantBroadcastDao(final IDBI dbi, final Clock clock) {
- super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, null, null), TenantBroadcastSqlDao.class);
+ public NoCachingTenantBroadcastDao(final IDBI dbi, final Clock clock, @Named(DefaultTenantModule.NO_CACHING_TENANT) final InternalCallContextFactory internalCallContextFactory) {
+ super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, null, null, internalCallContextFactory), TenantBroadcastSqlDao.class);
}
@Override
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/dao/NoCachingTenantDao.java b/tenant/src/main/java/org/killbill/billing/tenant/dao/NoCachingTenantDao.java
index a491f09..3913ae2 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/dao/NoCachingTenantDao.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/dao/NoCachingTenantDao.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -21,10 +21,14 @@ import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
+import javax.inject.Named;
+
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.tenant.api.Tenant;
import org.killbill.billing.tenant.api.TenantApiException;
+import org.killbill.billing.tenant.glue.DefaultTenantModule;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.entity.Pagination;
import org.killbill.billing.util.entity.dao.EntityDaoBase;
import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
@@ -35,7 +39,6 @@ import org.skife.jdbi.v2.IDBI;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
-import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
/**
@@ -51,8 +54,8 @@ import com.google.inject.Inject;
public class NoCachingTenantDao extends EntityDaoBase<TenantModelDao, Tenant, TenantApiException> implements TenantDao {
@Inject
- public NoCachingTenantDao(final IDBI dbi, final Clock clock) {
- super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, null, null), TenantSqlDao.class);
+ public NoCachingTenantDao(final IDBI dbi, final Clock clock, @Named(DefaultTenantModule.NO_CACHING_TENANT) final InternalCallContextFactory internalCallContextFactory) {
+ super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, null, null, internalCallContextFactory), TenantSqlDao.class);
}
@Override
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/glue/DefaultTenantModule.java b/tenant/src/main/java/org/killbill/billing/tenant/glue/DefaultTenantModule.java
index af1c9f7..c85a96f 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/glue/DefaultTenantModule.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/glue/DefaultTenantModule.java
@@ -34,8 +34,10 @@ import org.killbill.billing.tenant.dao.NoCachingTenantBroadcastDao;
import org.killbill.billing.tenant.dao.NoCachingTenantDao;
import org.killbill.billing.tenant.dao.TenantBroadcastDao;
import org.killbill.billing.tenant.dao.TenantDao;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.config.TenantConfig;
import org.killbill.billing.util.glue.KillBillModule;
+import org.killbill.billing.util.glue.NoCachingInternalCallContextFactoryProvider;
import org.skife.config.ConfigurationObjectFactory;
import com.google.inject.name.Names;
@@ -59,6 +61,7 @@ public class DefaultTenantModule extends KillBillModule implements TenantModule
bind(TenantDao.class).annotatedWith(Names.named(NO_CACHING_TENANT)).to(NoCachingTenantDao.class).asEagerSingleton();
bind(TenantBroadcastDao.class).to(DefaultTenantBroadcastDao.class).asEagerSingleton();
bind(TenantBroadcastDao.class).annotatedWith(Names.named(NO_CACHING_TENANT)).to(NoCachingTenantBroadcastDao.class).asEagerSingleton();
+ bind(InternalCallContextFactory.class).annotatedWith(Names.named(NO_CACHING_TENANT)).toProvider(NoCachingInternalCallContextFactoryProvider.class).asEagerSingleton();
}
public void installTenantUserApi() {
diff --git a/tenant/src/test/java/org/killbill/billing/tenant/dao/TestNoCachingTenantBroadcastDao.java b/tenant/src/test/java/org/killbill/billing/tenant/dao/TestNoCachingTenantBroadcastDao.java
index 4d6a43a..8796587 100644
--- a/tenant/src/test/java/org/killbill/billing/tenant/dao/TestNoCachingTenantBroadcastDao.java
+++ b/tenant/src/test/java/org/killbill/billing/tenant/dao/TestNoCachingTenantBroadcastDao.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -20,10 +20,7 @@ package org.killbill.billing.tenant.dao;
import java.util.List;
import java.util.UUID;
-import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.tenant.TenantTestSuiteWithEmbeddedDb;
-import org.killbill.billing.util.callcontext.CallOrigin;
-import org.killbill.billing.util.callcontext.UserType;
import org.testng.Assert;
import org.testng.annotations.Test;
@@ -33,13 +30,15 @@ public class TestNoCachingTenantBroadcastDao extends TenantTestSuiteWithEmbedded
public void testBasic() throws Exception {
final TenantBroadcastModelDao model = new TenantBroadcastModelDao(0L, "foo", UUID.randomUUID());
- final InternalCallContext context79 = createContext(79L);
- tenantBroadcastDao.create(model, context79);
+ internalCallContext.setTenantRecordId(79L);
+ tenantBroadcastDao.create(model, internalCallContext);
- final TenantBroadcastModelDao result1 = tenantBroadcastDao.getById(model.getId(), context79);
+ final TenantBroadcastModelDao result1 = tenantBroadcastDao.getById(model.getId(), internalCallContext);
Assert.assertEquals(result1.getTenantRecordId(), new Long(79L));
Assert.assertEquals(result1.getType(), "foo");
+ internalCallContext.reset();
+
final TenantBroadcastModelDao resultNull = tenantBroadcastDao.getById(model.getId(), internalCallContext);
Assert.assertNull(resultNull);
@@ -50,15 +49,15 @@ public class TestNoCachingTenantBroadcastDao extends TenantTestSuiteWithEmbedded
@Test(groups = "slow")
public void testLatestEntries() throws Exception {
+ internalCallContext.setTenantRecordId(81L);
- final InternalCallContext context79 = createContext(81L);
TenantBroadcastModelDao latestInsert = null;
for (int i = 0; i < 100; i++) {
final TenantBroadcastModelDao model = new TenantBroadcastModelDao(0L, "foo-" + i, UUID.randomUUID());
- tenantBroadcastDao.create(model, context79);
+ tenantBroadcastDao.create(model, internalCallContext);
latestInsert = model;
}
- final TenantBroadcastModelDao latestInsertRefreshed = tenantBroadcastDao.getById(latestInsert.getId(), context79);
+ final TenantBroadcastModelDao latestInsertRefreshed = tenantBroadcastDao.getById(latestInsert.getId(), internalCallContext);
final TenantBroadcastModelDao lastEntry = noCachingTenantBroadcastDao.getLatestEntry();
Assert.assertEquals(lastEntry.getRecordId(), latestInsertRefreshed.getRecordId());
@@ -69,17 +68,8 @@ public class TestNoCachingTenantBroadcastDao extends TenantTestSuiteWithEmbedded
Assert.assertEquals(result.size(), expectedEntries);
long i = 0;
- for (TenantBroadcastModelDao cur : result) {
+ for (final TenantBroadcastModelDao cur : result) {
Assert.assertEquals(cur.getRecordId().longValue(), (fromRecordId + i++ + 1L));
}
-
- }
-
- private InternalCallContext createContext(final Long tenantRecordId) {
- return new InternalCallContext(tenantRecordId, 0L, UUID.randomUUID(),
- UUID.randomUUID().toString(), CallOrigin.TEST,
- UserType.TEST, "Testing TestNoCachingTenantBroadcastDao", "This is a test for TestNoCachingTenantBroadcastDao",
- clock.getUTCNow(), clock.getUTCNow());
-
}
}
diff --git a/tenant/src/test/java/org/killbill/billing/tenant/glue/TestTenantModuleWithEmbeddedDB.java b/tenant/src/test/java/org/killbill/billing/tenant/glue/TestTenantModuleWithEmbeddedDB.java
index 0bae0d1..8c14902 100644
--- a/tenant/src/test/java/org/killbill/billing/tenant/glue/TestTenantModuleWithEmbeddedDB.java
+++ b/tenant/src/test/java/org/killbill/billing/tenant/glue/TestTenantModuleWithEmbeddedDB.java
@@ -19,6 +19,7 @@
package org.killbill.billing.tenant.glue;
import org.killbill.billing.GuicyKillbillTestWithEmbeddedDBModule;
+import org.killbill.billing.mock.glue.MockAccountModule;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.util.glue.NonEntityDaoModule;
import org.killbill.billing.util.glue.SecurityModule;
@@ -38,5 +39,6 @@ public class TestTenantModuleWithEmbeddedDB extends TestTenantModule {
install(new NonEntityDaoModule(configSource));
install(new SecurityModule(configSource));
install(new ShiroModuleNoDB(configSource));
+ install(new MockAccountModule(configSource));
}
}
usage/pom.xml 5(+5 -0)
diff --git a/usage/pom.xml b/usage/pom.xml
index 1993042..0845703 100644
--- a/usage/pom.xml
+++ b/usage/pom.xml
@@ -55,6 +55,11 @@
</dependency>
<dependency>
<groupId>org.kill-bill.billing</groupId>
+ <artifactId>killbill-account</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.kill-bill.billing</groupId>
<artifactId>killbill-api</artifactId>
</dependency>
<dependency>
diff --git a/usage/src/main/java/org/killbill/billing/usage/api/user/DefaultUsageUserApi.java b/usage/src/main/java/org/killbill/billing/usage/api/user/DefaultUsageUserApi.java
index 1feb447..45b0146 100644
--- a/usage/src/main/java/org/killbill/billing/usage/api/user/DefaultUsageUserApi.java
+++ b/usage/src/main/java/org/killbill/billing/usage/api/user/DefaultUsageUserApi.java
@@ -64,7 +64,7 @@ public class DefaultUsageUserApi implements UsageUserApi {
@Override
public RolledUpUsage getUsageForSubscription(final UUID subscriptionId, final String unitType, final LocalDate startDate, final LocalDate endDate, final TenantContext tenantContext) {
- final List<RolledUpUsageModelDao> usageForSubscription = rolledUpUsageDao.getUsageForSubscription(subscriptionId, startDate, endDate, unitType, internalCallContextFactory.createInternalTenantContext(tenantContext));
+ final List<RolledUpUsageModelDao> usageForSubscription = rolledUpUsageDao.getUsageForSubscription(subscriptionId, startDate, endDate, unitType, internalCallContextFactory.createInternalTenantContext(subscriptionId, ObjectType.SUBSCRIPTION, tenantContext));
final List<RolledUpUnit> rolledUpAmount = getRolledUpUnits(usageForSubscription);
return new DefaultRolledUpUsage(subscriptionId, startDate, endDate, rolledUpAmount);
}
diff --git a/usage/src/main/resources/org/killbill/billing/usage/ddl.sql b/usage/src/main/resources/org/killbill/billing/usage/ddl.sql
index 9415162..44837a9 100644
--- a/usage/src/main/resources/org/killbill/billing/usage/ddl.sql
+++ b/usage/src/main/resources/org/killbill/billing/usage/ddl.sql
@@ -5,7 +5,7 @@ CREATE TABLE rolled_up_usage (
record_id serial unique,
id varchar(36) NOT NULL,
subscription_id varchar(36),
- unit_type varchar(50),
+ unit_type varchar(255),
record_date date NOT NULL,
amount bigint NOT NULL,
created_by varchar(50) NOT NULL,
diff --git a/usage/src/test/java/org/killbill/billing/usage/glue/TestUsageModuleWithEmbeddedDB.java b/usage/src/test/java/org/killbill/billing/usage/glue/TestUsageModuleWithEmbeddedDB.java
index 1e71a18..15078d8 100644
--- a/usage/src/test/java/org/killbill/billing/usage/glue/TestUsageModuleWithEmbeddedDB.java
+++ b/usage/src/test/java/org/killbill/billing/usage/glue/TestUsageModuleWithEmbeddedDB.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -19,6 +19,7 @@
package org.killbill.billing.usage.glue;
import org.killbill.billing.GuicyKillbillTestWithEmbeddedDBModule;
+import org.killbill.billing.account.glue.DefaultAccountModule;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.util.glue.CacheModule;
import org.killbill.billing.util.glue.NonEntityDaoModule;
@@ -36,5 +37,6 @@ public class TestUsageModuleWithEmbeddedDB extends TestUsageModule {
install(new GuicyKillbillTestWithEmbeddedDBModule(configSource));
install(new CacheModule(configSource));
install(new NonEntityDaoModule(configSource));
+ install(new DefaultAccountModule(configSource));
}
}
diff --git a/util/src/main/java/org/killbill/billing/util/account/AccountDateTimeUtils.java b/util/src/main/java/org/killbill/billing/util/account/AccountDateTimeUtils.java
new file mode 100644
index 0000000..6ab4322
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/account/AccountDateTimeUtils.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016 Groupon, Inc
+ * Copyright 2016 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.account;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.killbill.billing.account.api.Account;
+
+public abstract class AccountDateTimeUtils {
+
+ public static DateTimeZone getFixedOffsetTimeZone(final Account account) {
+ final DateTimeZone referenceDateTimeZone = account.getTimeZone();
+ final DateTime referenceDateTime = getReferenceDateTime(account);
+
+ // Check if DST was in effect at the reference date time
+ final boolean shouldUseDST = !referenceDateTimeZone.isStandardOffset(referenceDateTime.getMillis());
+ if (shouldUseDST) {
+ return DateTimeZone.forOffsetMillis(referenceDateTimeZone.getOffset(referenceDateTime.getMillis()));
+ } else {
+ return DateTimeZone.forOffsetMillis(referenceDateTimeZone.getStandardOffset(referenceDateTime.getMillis()));
+ }
+ }
+
+ public static DateTime getReferenceDateTime(final Account account) {
+ return account.getCreatedDate();
+ }
+}
diff --git a/util/src/main/java/org/killbill/billing/util/audit/api/DefaultAuditUserApi.java b/util/src/main/java/org/killbill/billing/util/audit/api/DefaultAuditUserApi.java
index 9c035a3..4692059 100644
--- a/util/src/main/java/org/killbill/billing/util/audit/api/DefaultAuditUserApi.java
+++ b/util/src/main/java/org/killbill/billing/util/audit/api/DefaultAuditUserApi.java
@@ -84,7 +84,7 @@ public class DefaultAuditUserApi implements AuditUserApi {
return ImmutableList.<AuditLog>of();
}
- return auditDao.getAuditLogsForId(tableName, objectId, auditLevel, internalCallContextFactory.createInternalTenantContext(context));
+ return auditDao.getAuditLogsForId(tableName, objectId, auditLevel, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
}
private TableName getTableNameFromObjectType(final ObjectType objectType) {
diff --git a/util/src/main/java/org/killbill/billing/util/audit/dao/DefaultAuditDao.java b/util/src/main/java/org/killbill/billing/util/audit/dao/DefaultAuditDao.java
index 12a33e9..13b75f5 100644
--- a/util/src/main/java/org/killbill/billing/util/audit/dao/DefaultAuditDao.java
+++ b/util/src/main/java/org/killbill/billing/util/audit/dao/DefaultAuditDao.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2012 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -33,6 +35,7 @@ import org.killbill.billing.util.audit.DefaultAccountAuditLogs;
import org.killbill.billing.util.audit.DefaultAccountAuditLogsForObjectType;
import org.killbill.billing.util.audit.DefaultAuditLog;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.dao.NonEntitySqlDao;
import org.killbill.billing.util.dao.RecordIdIdMappings;
@@ -55,9 +58,9 @@ public class DefaultAuditDao implements AuditDao {
private final EntitySqlDaoTransactionalJdbiWrapper transactionalSqlDao;
@Inject
- public DefaultAuditDao(final IDBI dbi, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao) {
+ public DefaultAuditDao(final IDBI dbi, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory) {
this.nonEntitySqlDao = dbi.onDemand(NonEntitySqlDao.class);
- this.transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao);
+ this.transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory);
}
@Override
diff --git a/util/src/main/java/org/killbill/billing/util/cache/OverriddenPlanCacheLoader.java b/util/src/main/java/org/killbill/billing/util/cache/OverriddenPlanCacheLoader.java
index ab2e833..52c4a07 100644
--- a/util/src/main/java/org/killbill/billing/util/cache/OverriddenPlanCacheLoader.java
+++ b/util/src/main/java/org/killbill/billing/util/cache/OverriddenPlanCacheLoader.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -17,15 +17,12 @@
package org.killbill.billing.util.cache;
-import java.util.List;
-
import javax.inject.Inject;
import javax.inject.Singleton;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.CatalogApiException;
import org.killbill.billing.catalog.api.StaticCatalog;
-import org.killbill.billing.tenant.api.TenantInternalApi;
import org.killbill.billing.util.cache.Cachable.CacheType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -35,9 +32,8 @@ public class OverriddenPlanCacheLoader extends BaseCacheLoader {
private final Logger log = LoggerFactory.getLogger(OverriddenPlanCacheLoader.class);
-
@Inject
- public OverriddenPlanCacheLoader(final TenantInternalApi tenantApi) {
+ public OverriddenPlanCacheLoader() {
super();
}
diff --git a/util/src/main/java/org/killbill/billing/util/callcontext/InternalCallContextFactory.java b/util/src/main/java/org/killbill/billing/util/callcontext/InternalCallContextFactory.java
index d4367b5..27a6889 100644
--- a/util/src/main/java/org/killbill/billing/util/callcontext/InternalCallContextFactory.java
+++ b/util/src/main/java/org/killbill/billing/util/callcontext/InternalCallContextFactory.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2012 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -24,7 +24,11 @@ import javax.annotation.Nullable;
import javax.inject.Inject;
import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
import org.killbill.billing.ObjectType;
+import org.killbill.billing.account.api.AccountApiException;
+import org.killbill.billing.account.api.ImmutableAccountData;
+import org.killbill.billing.account.api.ImmutableAccountInternalApi;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.util.cache.Cachable.CacheType;
@@ -32,19 +36,24 @@ import org.killbill.billing.util.cache.CacheControllerDispatcher;
import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.clock.Clock;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
// Internal contexts almost always expect accountRecordId and tenantRecordId to be populated
public class InternalCallContextFactory {
public static final long INTERNAL_TENANT_RECORD_ID = 0L;
+ private final ImmutableAccountInternalApi accountInternalApi;
private final Clock clock;
private final NonEntityDao nonEntityDao;
private final CacheControllerDispatcher cacheControllerDispatcher;
@Inject
- public InternalCallContextFactory(final Clock clock, final NonEntityDao nonEntityDao, final CacheControllerDispatcher cacheControllerDispatcher) {
+ public InternalCallContextFactory(@Nullable final ImmutableAccountInternalApi accountInternalApi,
+ final Clock clock,
+ final NonEntityDao nonEntityDao,
+ @Nullable final CacheControllerDispatcher cacheControllerDispatcher) {
+ this.accountInternalApi = accountInternalApi;
this.clock = clock;
this.nonEntityDao = nonEntityDao;
this.cacheControllerDispatcher = cacheControllerDispatcher;
@@ -77,7 +86,7 @@ public class InternalCallContextFactory {
* @param context tenant callcontext (tenantId can be null only if multi-tenancy is disabled)
* @return internal tenant callcontext
*/
- public InternalTenantContext createInternalTenantContext(final TenantContext context) {
+ public InternalTenantContext createInternalTenantContextWithoutAccountRecordId(final TenantContext context) {
// If tenant id is null, this will default to the default tenant record id (multi-tenancy disabled)
final Long tenantRecordId = getTenantRecordIdSafe(context);
return createInternalTenantContext(tenantRecordId, null);
@@ -120,8 +129,11 @@ public class InternalCallContextFactory {
* @return internal tenant callcontext
*/
public InternalTenantContext createInternalTenantContext(final Long tenantRecordId, @Nullable final Long accountRecordId) {
- //Preconditions.checkNotNull(tenantRecordId, "tenantRecordId cannot be null");
- return new InternalTenantContext(tenantRecordId, accountRecordId);
+ if (accountRecordId == null) {
+ return new InternalTenantContext(tenantRecordId);
+ } else {
+ return new InternalTenantContext(tenantRecordId, accountRecordId, getFixedOffsetTimeZone(accountRecordId, tenantRecordId), getReferenceTime(accountRecordId, tenantRecordId));
+ }
}
//
@@ -158,15 +170,14 @@ public class InternalCallContextFactory {
return createInternalCallContext(objectId, objectType, context.getUserName(), context.getCallOrigin(),
context.getUserType(), context.getUserToken(), context.getReasonCode(), context.getComments(),
- context.getCreatedDate(), context.getUpdatedDate(), context);
+ context);
}
// Used by the payment retry service
public InternalCallContext createInternalCallContext(final UUID objectId, final ObjectType objectType, final String userName,
final CallOrigin callOrigin, final UserType userType, @Nullable final UUID userToken, final Long tenantRecordId) {
final Long accountRecordId = getAccountRecordIdSafe(objectId, objectType, tenantRecordId);
- return createInternalCallContext(tenantRecordId, accountRecordId, userName, callOrigin, userType, userToken,
- null, null, clock.getUTCNow(), clock.getUTCNow());
+ return createInternalCallContext(tenantRecordId, accountRecordId, userName, callOrigin, userType, userToken, null, null);
}
/**
@@ -175,17 +186,16 @@ public class InternalCallContextFactory {
* This is used by notification queue and persistent bus - accountRecordId is expected to be non null
*
* @param tenantRecordId tenant record id - if null, the default tenant record id value will be used
- * @param accountRecordId account record id (cannot be null)
+ * @param accountRecordId account record id (can be null in specific use-cases, e.g. config change events in BeatrixListener)
* @param userName user name
* @param callOrigin call origin
* @param userType user type
* @param userToken user token, if any
* @return internal call callcontext
*/
- public InternalCallContext createInternalCallContext(@Nullable final Long tenantRecordId, final Long accountRecordId, final String userName,
+ public InternalCallContext createInternalCallContext(@Nullable final Long tenantRecordId, @Nullable final Long accountRecordId, final String userName,
final CallOrigin callOrigin, final UserType userType, @Nullable final UUID userToken) {
- return new InternalCallContext(tenantRecordId, accountRecordId, userToken, userName, callOrigin, userType, null, null,
- clock.getUTCNow(), clock.getUTCNow());
+ return createInternalCallContext(tenantRecordId, accountRecordId, userName, callOrigin, userType, userToken, null, null);
}
/**
@@ -197,38 +207,78 @@ public class InternalCallContextFactory {
* @param context original call callcontext
* @return internal call callcontext
*/
- public InternalCallContext createInternalCallContext(final CallContext context) {
+ public InternalCallContext createInternalCallContextWithoutAccountRecordId(final CallContext context) {
// If tenant id is null, this will default to the default tenant record id (multi-tenancy disabled)
final Long tenantRecordId = getTenantRecordIdSafe(context);
- return new InternalCallContext(tenantRecordId, null, context);
+ return new InternalCallContext(tenantRecordId, context, clock.getUTCNow());
}
// Used when we need to re-hydrate the callcontext with the account_record_id (when creating the account)
public InternalCallContext createInternalCallContext(final Long accountRecordId, final InternalCallContext context) {
- return new InternalCallContext(context.getTenantRecordId(), accountRecordId, context.getUserToken(), context.getCreatedBy(),
- context.getCallOrigin(), context.getContextUserType(), context.getReasonCode(), context.getComments(),
- context.getCreatedDate(), context.getUpdatedDate());
+ final DateTimeZone fixedOffsetTimeZone = getFixedOffsetTimeZone(accountRecordId, context.getTenantRecordId());
+ final DateTime referenceTime = getReferenceTime(accountRecordId, context.getTenantRecordId());
+ return new InternalCallContext(context, accountRecordId, fixedOffsetTimeZone, referenceTime, clock.getUTCNow());
}
private InternalCallContext createInternalCallContext(final UUID objectId, final ObjectType objectType, final String userName,
final CallOrigin callOrigin, final UserType userType, @Nullable final UUID userToken,
- @Nullable final String reasonCode, @Nullable final String comment, final DateTime createdDate,
- final DateTime updatedDate, final TenantContext tenantContext) {
+ @Nullable final String reasonCode, @Nullable final String comment,
+ final TenantContext tenantContext) {
final Long tenantRecordId = getTenantRecordIdSafe(tenantContext);
final Long accountRecordId = getAccountRecordIdSafe(objectId, objectType, tenantContext);
return createInternalCallContext(tenantRecordId, accountRecordId, userName, callOrigin, userType, userToken,
- reasonCode, comment, createdDate, updatedDate);
+ reasonCode, comment);
}
- private InternalCallContext createInternalCallContext(@Nullable final Long tenantRecordId, final Long accountRecordId, final String userName,
+ private InternalCallContext createInternalCallContext(@Nullable final Long tenantRecordId, @Nullable final Long accountRecordId, final String userName,
final CallOrigin callOrigin, final UserType userType, @Nullable final UUID userToken,
- @Nullable final String reasonCode, @Nullable final String comment, final DateTime createdDate,
- final DateTime updatedDate) {
- //Preconditions.checkNotNull(accountRecordId, "accountRecordId cannot be null");
- final Long nonNulTenantRecordId = Objects.firstNonNull(tenantRecordId, INTERNAL_TENANT_RECORD_ID);
+ @Nullable final String reasonCode, @Nullable final String comment) {
+ final Long nonNulTenantRecordId = MoreObjects.firstNonNull(tenantRecordId, INTERNAL_TENANT_RECORD_ID);
+ final DateTimeZone fixedOffsetTimeZone = getFixedOffsetTimeZone(accountRecordId, tenantRecordId);
+ final DateTime referenceTime = getReferenceTime(accountRecordId, tenantRecordId);
+
+ return new InternalCallContext(nonNulTenantRecordId,
+ accountRecordId,
+ fixedOffsetTimeZone,
+ referenceTime,
+ userToken,
+ userName,
+ callOrigin,
+ userType,
+ reasonCode,
+ comment,
+ clock.getUTCNow(),
+ clock.getUTCNow());
+ }
+
+ private DateTimeZone getFixedOffsetTimeZone(@Nullable final Long accountRecordId, final Long tenantRecordId) {
+ if (accountRecordId == null || accountInternalApi == null) {
+ return null;
+ }
+
+ final ImmutableAccountData immutableAccountData = getImmutableAccountData(accountRecordId, tenantRecordId);
+ // Will be null while creating the account
+ return immutableAccountData == null ? null : immutableAccountData.getFixedOffsetTimeZone();
+ }
- return new InternalCallContext(nonNulTenantRecordId, accountRecordId, userToken, userName, callOrigin, userType, reasonCode, comment,
- createdDate, updatedDate);
+ private DateTime getReferenceTime(@Nullable final Long accountRecordId, final Long tenantRecordId) {
+ if (accountRecordId == null || accountInternalApi == null) {
+ return null;
+ }
+
+ final ImmutableAccountData immutableAccountData = getImmutableAccountData(accountRecordId, tenantRecordId);
+ // Will be null while creating the account
+ return immutableAccountData == null ? null : immutableAccountData.getReferenceTime();
+ }
+
+ private ImmutableAccountData getImmutableAccountData(@Nullable final Long accountRecordId, final Long tenantRecordId) {
+ final InternalTenantContext tmp = new InternalTenantContext(tenantRecordId, accountRecordId, null, null);
+
+ try {
+ return accountInternalApi.getImmutableAccountDataByRecordId(accountRecordId, tmp);
+ } catch (final AccountApiException e) {
+ return null;
+ }
}
//
@@ -239,7 +289,7 @@ public class InternalCallContextFactory {
public UUID getAccountId(final UUID objectId, final ObjectType objectType, final TenantContext context) {
final Long accountRecordId = getAccountRecordIdSafe(objectId, objectType, context);
if (accountRecordId != null) {
- return nonEntityDao.retrieveIdFromObject(accountRecordId, ObjectType.ACCOUNT, cacheControllerDispatcher.getCacheController(CacheType.OBJECT_ID));
+ return nonEntityDao.retrieveIdFromObject(accountRecordId, ObjectType.ACCOUNT, cacheControllerDispatcher == null ? null : cacheControllerDispatcher.getCacheController(CacheType.OBJECT_ID));
} else {
return null;
}
@@ -249,7 +299,7 @@ public class InternalCallContextFactory {
public Long getRecordIdFromObject(final UUID objectId, final ObjectType objectType, final TenantContext context) {
try {
if (objectBelongsToTheRightTenant(objectId, objectType, context)) {
- return nonEntityDao.retrieveRecordIdFromObject(objectId, objectType, cacheControllerDispatcher.getCacheController(CacheType.RECORD_ID));
+ return nonEntityDao.retrieveRecordIdFromObject(objectId, objectType, cacheControllerDispatcher == null ? null : cacheControllerDispatcher.getCacheController(CacheType.RECORD_ID));
} else {
return null;
}
@@ -290,7 +340,7 @@ public class InternalCallContextFactory {
}
private UUID getTenantIdSafe(final InternalTenantContext context) {
- return nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT, cacheControllerDispatcher.getCacheController(CacheType.OBJECT_ID));
+ return nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT, cacheControllerDispatcher == null ? null : cacheControllerDispatcher.getCacheController(CacheType.OBJECT_ID));
}
//
@@ -318,14 +368,14 @@ public class InternalCallContextFactory {
//
private Long getAccountRecordIdUnsafe(final UUID objectId, final ObjectType objectType) {
- return nonEntityDao.retrieveAccountRecordIdFromObject(objectId, objectType, cacheControllerDispatcher.getCacheController(CacheType.ACCOUNT_RECORD_ID));
+ return nonEntityDao.retrieveAccountRecordIdFromObject(objectId, objectType, cacheControllerDispatcher == null ? null : cacheControllerDispatcher.getCacheController(CacheType.ACCOUNT_RECORD_ID));
}
private Long getTenantRecordIdUnsafe(final UUID objectId, final ObjectType objectType) {
- return nonEntityDao.retrieveTenantRecordIdFromObject(objectId, objectType, cacheControllerDispatcher.getCacheController(CacheType.TENANT_RECORD_ID));
+ return nonEntityDao.retrieveTenantRecordIdFromObject(objectId, objectType, cacheControllerDispatcher == null ? null : cacheControllerDispatcher.getCacheController(CacheType.TENANT_RECORD_ID));
}
- private static final class ObjectDoesNotExist extends IllegalStateException {
+ public static final class ObjectDoesNotExist extends IllegalStateException {
public ObjectDoesNotExist(final String s) {
super(s);
diff --git a/util/src/main/java/org/killbill/billing/util/customfield/api/DefaultCustomFieldUserApi.java b/util/src/main/java/org/killbill/billing/util/customfield/api/DefaultCustomFieldUserApi.java
index 5abac28..23acc3e 100644
--- a/util/src/main/java/org/killbill/billing/util/customfield/api/DefaultCustomFieldUserApi.java
+++ b/util/src/main/java/org/killbill/billing/util/customfield/api/DefaultCustomFieldUserApi.java
@@ -69,7 +69,7 @@ public class DefaultCustomFieldUserApi implements CustomFieldUserApi {
new SourcePaginationBuilder<CustomFieldModelDao, CustomFieldApiException>() {
@Override
public Pagination<CustomFieldModelDao> build() {
- return customFieldDao.searchCustomFields(searchKey, offset, limit, internalCallContextFactory.createInternalTenantContext(context));
+ return customFieldDao.searchCustomFields(searchKey, offset, limit, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
}
},
CUSTOM_FIELD_MODEL_DAO_CUSTOM_FIELD_FUNCTION);
@@ -81,7 +81,7 @@ public class DefaultCustomFieldUserApi implements CustomFieldUserApi {
new SourcePaginationBuilder<CustomFieldModelDao, CustomFieldApiException>() {
@Override
public Pagination<CustomFieldModelDao> build() {
- return customFieldDao.get(offset, limit, internalCallContextFactory.createInternalTenantContext(context));
+ return customFieldDao.get(offset, limit, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
}
},
CUSTOM_FIELD_MODEL_DAO_CUSTOM_FIELD_FUNCTION);
@@ -133,7 +133,7 @@ public class DefaultCustomFieldUserApi implements CustomFieldUserApi {
@Override
public List<CustomField> getCustomFieldsForObject(final UUID objectId, final ObjectType objectType, final TenantContext context) {
- return withCustomFieldsTransform(customFieldDao.getCustomFieldsForObject(objectId, objectType, internalCallContextFactory.createInternalTenantContext(context)));
+ return withCustomFieldsTransform(customFieldDao.getCustomFieldsForObject(objectId, objectType, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context)));
}
@Override
diff --git a/util/src/main/java/org/killbill/billing/util/customfield/dao/DefaultCustomFieldDao.java b/util/src/main/java/org/killbill/billing/util/customfield/dao/DefaultCustomFieldDao.java
index c4ac0b2..1416fb4 100644
--- a/util/src/main/java/org/killbill/billing/util/customfield/dao/DefaultCustomFieldDao.java
+++ b/util/src/main/java/org/killbill/billing/util/customfield/dao/DefaultCustomFieldDao.java
@@ -1,7 +1,9 @@
/*
- * Copyright 2010-2011 Ning, Inc.
+ * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -22,6 +24,7 @@ import java.util.UUID;
import javax.annotation.Nullable;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.skife.jdbi.v2.IDBI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -60,8 +63,9 @@ public class DefaultCustomFieldDao extends EntityDaoBase<CustomFieldModelDao, Cu
private final PersistentBus bus;
@Inject
- public DefaultCustomFieldDao(final IDBI dbi, final Clock clock, final CacheControllerDispatcher controllerDispatcher, final NonEntityDao nonEntityDao, final PersistentBus bus) {
- super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, controllerDispatcher, nonEntityDao), CustomFieldSqlDao.class);
+ public DefaultCustomFieldDao(final IDBI dbi, final Clock clock, final CacheControllerDispatcher controllerDispatcher,
+ final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory, final PersistentBus bus) {
+ super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, controllerDispatcher, nonEntityDao, internalCallContextFactory), CustomFieldSqlDao.class);
this.bus = bus;
}
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java
index 8be616a..e70fcba 100644
--- a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2012 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -19,6 +19,7 @@
package org.killbill.billing.util.entity.dao;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.entity.Entity;
import org.killbill.clock.Clock;
@@ -37,12 +38,15 @@ public class EntitySqlDaoTransactionalJdbiWrapper {
private final Clock clock;
private final CacheControllerDispatcher cacheControllerDispatcher;
private final NonEntityDao nonEntityDao;
+ private final InternalCallContextFactory internalCallContextFactory;
- public EntitySqlDaoTransactionalJdbiWrapper(final IDBI dbi, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao) {
+ public EntitySqlDaoTransactionalJdbiWrapper(final IDBI dbi, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher,
+ final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory) {
this.dbi = dbi;
this.clock = clock;
this.cacheControllerDispatcher = cacheControllerDispatcher;
this.nonEntityDao = nonEntityDao;
+ this.internalCallContextFactory = internalCallContextFactory;
}
class JdbiTransaction<ReturnType, M extends EntityModelDao<E>, E extends Entity> implements Transaction<ReturnType, EntitySqlDao<M, E>> {
@@ -57,7 +61,7 @@ public class EntitySqlDaoTransactionalJdbiWrapper {
@Override
public ReturnType inTransaction(final EntitySqlDao<M, E> transactionalSqlDao, final TransactionStatus status) throws Exception {
- final EntitySqlDaoWrapperFactory factoryEntitySqlDao = new EntitySqlDaoWrapperFactory(h, clock, cacheControllerDispatcher, nonEntityDao);
+ final EntitySqlDaoWrapperFactory factoryEntitySqlDao = new EntitySqlDaoWrapperFactory(h, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory);
return entitySqlDaoTransactionWrapper.inTransaction(factoryEntitySqlDao);
}
}
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperFactory.java b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperFactory.java
index 0dc33c6..2d29ee7 100644
--- a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperFactory.java
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperFactory.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2012 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -21,6 +21,7 @@ package org.killbill.billing.util.entity.dao;
import java.lang.reflect.Proxy;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.entity.Entity;
import org.killbill.clock.Clock;
@@ -40,12 +41,14 @@ public class EntitySqlDaoWrapperFactory {
private final CacheControllerDispatcher cacheControllerDispatcher;
private final NonEntityDao nonEntityDao;
+ private final InternalCallContextFactory internalCallContextFactory;
- public EntitySqlDaoWrapperFactory(final Handle handle, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao) {
+ public EntitySqlDaoWrapperFactory(final Handle handle, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory) {
this.handle = handle;
this.clock = clock;
this.cacheControllerDispatcher = cacheControllerDispatcher;
this.nonEntityDao = nonEntityDao;
+ this.internalCallContextFactory = internalCallContextFactory;
}
/**
@@ -73,7 +76,7 @@ public class EntitySqlDaoWrapperFactory {
final ClassLoader classLoader = newSqlDao.getClass().getClassLoader();
final Class[] interfacesToImplement = {newSqlDaoClass};
final EntitySqlDaoWrapperInvocationHandler<NewSqlDao, NewEntityModelDao, NewEntity> wrapperInvocationHandler =
- new EntitySqlDaoWrapperInvocationHandler<NewSqlDao, NewEntityModelDao, NewEntity>(newSqlDaoClass, newSqlDao, handle, clock, cacheControllerDispatcher, nonEntityDao);
+ new EntitySqlDaoWrapperInvocationHandler<NewSqlDao, NewEntityModelDao, NewEntity>(newSqlDaoClass, newSqlDao, handle, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory);
final Object newSqlDaoObject = Proxy.newProxyInstance(classLoader, interfacesToImplement, wrapperInvocationHandler);
return newSqlDaoClass.cast(newSqlDaoObject);
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
index 9793364..dde8ec7 100644
--- a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2012 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -43,6 +43,7 @@ import org.killbill.billing.util.cache.CachableKey;
import org.killbill.billing.util.cache.CacheController;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
import org.killbill.billing.util.cache.CacheLoaderArgument;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.dao.EntityAudit;
import org.killbill.billing.util.dao.EntityHistoryModelDao;
import org.killbill.billing.util.dao.NonEntityDao;
@@ -89,6 +90,7 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>,
private final CacheControllerDispatcher cacheControllerDispatcher;
private final Clock clock;
private final NonEntityDao nonEntityDao;
+ private final InternalCallContextFactory internalCallContextFactory;
private final Profiling prof;
public EntitySqlDaoWrapperInvocationHandler(final Class<S> sqlDaoClass,
@@ -97,13 +99,15 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>,
final Clock clock,
// Special DAO that don't require caching can invoke EntitySqlDaoWrapperInvocationHandler with no caching (e.g NoCachingTenantDao)
@Nullable final CacheControllerDispatcher cacheControllerDispatcher,
- @Nullable final NonEntityDao nonEntityDao) {
+ @Nullable final NonEntityDao nonEntityDao,
+ final InternalCallContextFactory internalCallContextFactory) {
this.sqlDaoClass = sqlDaoClass;
this.sqlDao = sqlDao;
this.handle = handle;
this.clock = clock;
this.cacheControllerDispatcher = cacheControllerDispatcher;
this.nonEntityDao = nonEntityDao;
+ this.internalCallContextFactory = internalCallContextFactory;
this.prof = new Profiling<Object, Throwable>();
}
@@ -465,7 +469,7 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>,
final InternalCallContext context;
// Populate the account record id when creating the account record
if (TableName.ACCOUNT.equals(tableName) && ChangeType.INSERT.equals(changeType)) {
- context = new InternalCallContext(contextMaybeWithoutAccountRecordId, entityRecordId);
+ context = internalCallContextFactory.createInternalCallContext(entityRecordId, contextMaybeWithoutAccountRecordId);
} else {
context = contextMaybeWithoutAccountRecordId;
}
diff --git a/util/src/main/java/org/killbill/billing/util/tag/api/DefaultTagUserApi.java b/util/src/main/java/org/killbill/billing/util/tag/api/DefaultTagUserApi.java
index 44f530c..068c90a 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/api/DefaultTagUserApi.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/api/DefaultTagUserApi.java
@@ -74,7 +74,7 @@ public class DefaultTagUserApi implements TagUserApi {
@Override
public List<TagDefinition> getTagDefinitions(final TenantContext context) {
- return ImmutableList.<TagDefinition>copyOf(Collections2.transform(tagDefinitionDao.getTagDefinitions(internalCallContextFactory.createInternalTenantContext(context)),
+ return ImmutableList.<TagDefinition>copyOf(Collections2.transform(tagDefinitionDao.getTagDefinitions(internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context)),
new Function<TagDefinitionModelDao, TagDefinition>() {
@Override
public TagDefinition apply(final TagDefinitionModelDao input) {
@@ -88,26 +88,26 @@ public class DefaultTagUserApi implements TagUserApi {
if (definitionName.matches(".*[A-Z].*")) {
throw new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_HAS_UPPERCASE, definitionName);
}
- final TagDefinitionModelDao tagDefinitionModelDao = tagDefinitionDao.create(definitionName, description, internalCallContextFactory.createInternalCallContext(context));
+ final TagDefinitionModelDao tagDefinitionModelDao = tagDefinitionDao.create(definitionName, description, internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(context));
return new DefaultTagDefinition(tagDefinitionModelDao, TagModelDaoHelper.isControlTag(tagDefinitionModelDao.getName()));
}
@Override
public void deleteTagDefinition(final UUID definitionId, final CallContext context) throws TagDefinitionApiException {
- tagDefinitionDao.deleteById(definitionId, internalCallContextFactory.createInternalCallContext(context));
+ tagDefinitionDao.deleteById(definitionId, internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(context));
}
@Override
public TagDefinition getTagDefinition(final UUID tagDefinitionId, final TenantContext context)
throws TagDefinitionApiException {
- final TagDefinitionModelDao tagDefinitionModelDao = tagDefinitionDao.getById(tagDefinitionId, internalCallContextFactory.createInternalTenantContext(context));
+ final TagDefinitionModelDao tagDefinitionModelDao = tagDefinitionDao.getById(tagDefinitionId, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
return new DefaultTagDefinition(tagDefinitionModelDao, TagModelDaoHelper.isControlTag(tagDefinitionModelDao.getName()));
}
@Override
public List<TagDefinition> getTagDefinitions(final Collection<UUID> tagDefinitionIds, final TenantContext context)
throws TagDefinitionApiException {
- return ImmutableList.<TagDefinition>copyOf(Collections2.transform(tagDefinitionDao.getByIds(tagDefinitionIds, internalCallContextFactory.createInternalTenantContext(context)),
+ return ImmutableList.<TagDefinition>copyOf(Collections2.transform(tagDefinitionDao.getByIds(tagDefinitionIds, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context)),
new Function<TagDefinitionModelDao, TagDefinition>() {
@Override
public TagDefinition apply(final TagDefinitionModelDao input) {
@@ -148,7 +148,7 @@ public class DefaultTagUserApi implements TagUserApi {
new SourcePaginationBuilder<TagModelDao, TagApiException>() {
@Override
public Pagination<TagModelDao> build() {
- return tagDao.searchTags(searchKey, offset, limit, internalCallContextFactory.createInternalTenantContext(context));
+ return tagDao.searchTags(searchKey, offset, limit, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
}
},
TAG_MODEL_DAO_TAG_FUNCTION);
@@ -160,7 +160,7 @@ public class DefaultTagUserApi implements TagUserApi {
new SourcePaginationBuilder<TagModelDao, TagApiException>() {
@Override
public Pagination<TagModelDao> build() {
- return tagDao.get(offset, limit, internalCallContextFactory.createInternalTenantContext(context));
+ return tagDao.get(offset, limit, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
}
},
TAG_MODEL_DAO_TAG_FUNCTION);
@@ -177,13 +177,13 @@ public class DefaultTagUserApi implements TagUserApi {
@Override
public TagDefinition getTagDefinitionForName(final String tagDefinitionName, final TenantContext context)
throws TagDefinitionApiException {
- return new DefaultTagDefinition(tagDefinitionDao.getByName(tagDefinitionName, internalCallContextFactory.createInternalTenantContext(context)),
+ return new DefaultTagDefinition(tagDefinitionDao.getByName(tagDefinitionName, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context)),
TagModelDaoHelper.isControlTag(tagDefinitionName));
}
@Override
public List<Tag> getTagsForObject(final UUID objectId, final ObjectType objectType, final boolean includedDeleted, final TenantContext context) {
- return withModelTransform(tagDao.getTagsForObject(objectId, objectType, includedDeleted, internalCallContextFactory.createInternalTenantContext(context)));
+ return withModelTransform(tagDao.getTagsForObject(objectId, objectType, includedDeleted, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context)));
}
@Override
diff --git a/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDao.java b/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDao.java
index 865af9d..62543e5 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDao.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDao.java
@@ -1,7 +1,9 @@
/*
- * Copyright 2010-2012 Ning, Inc.
+ * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -20,6 +22,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.UUID;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.skife.jdbi.v2.IDBI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -62,8 +65,8 @@ public class DefaultTagDao extends EntityDaoBase<TagModelDao, Tag, TagApiExcepti
@Inject
public DefaultTagDao(final IDBI dbi, final TagEventBuilder tagEventBuilder, final PersistentBus bus, final Clock clock,
- final CacheControllerDispatcher controllerDispatcher, final NonEntityDao nonEntityDao) {
- super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, controllerDispatcher, nonEntityDao), TagSqlDao.class);
+ final CacheControllerDispatcher controllerDispatcher, final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory) {
+ super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, controllerDispatcher, nonEntityDao, internalCallContextFactory), TagSqlDao.class);
this.tagEventBuilder = tagEventBuilder;
this.bus = bus;
}
diff --git a/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDefinitionDao.java b/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDefinitionDao.java
index 2eb20fd..9039976 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDefinitionDao.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDefinitionDao.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2011 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -22,6 +24,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.skife.jdbi.v2.IDBI;
import org.skife.jdbi.v2.exceptions.TransactionFailedException;
import org.slf4j.Logger;
@@ -60,8 +63,8 @@ public class DefaultTagDefinitionDao extends EntityDaoBase<TagDefinitionModelDao
@Inject
public DefaultTagDefinitionDao(final IDBI dbi, final TagEventBuilder tagEventBuilder, final PersistentBus bus, final Clock clock,
- final CacheControllerDispatcher controllerDispatcher, final NonEntityDao nonEntityDao) {
- super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, controllerDispatcher, nonEntityDao), TagDefinitionSqlDao.class);
+ final CacheControllerDispatcher controllerDispatcher, final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory) {
+ super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, controllerDispatcher, nonEntityDao, internalCallContextFactory), TagDefinitionSqlDao.class);
this.tagEventBuilder = tagEventBuilder;
this.bus = bus;
}
diff --git a/util/src/main/java/org/killbill/billing/util/userrequest/CompletionUserRequestBase.java b/util/src/main/java/org/killbill/billing/util/userrequest/CompletionUserRequestBase.java
index da85734..5114def 100644
--- a/util/src/main/java/org/killbill/billing/util/userrequest/CompletionUserRequestBase.java
+++ b/util/src/main/java/org/killbill/billing/util/userrequest/CompletionUserRequestBase.java
@@ -25,6 +25,7 @@ import java.util.concurrent.TimeoutException;
import org.killbill.billing.events.AccountChangeInternalEvent;
import org.killbill.billing.events.AccountCreationInternalEvent;
+import org.killbill.billing.events.BlockingTransitionInternalEvent;
import org.killbill.billing.events.BusInternalEvent;
import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
import org.killbill.billing.events.InvoiceCreationInternalEvent;
@@ -105,6 +106,9 @@ public class CompletionUserRequestBase implements CompletionUserRequest {
case ACCOUNT_CHANGE:
onAccountChange((AccountChangeInternalEvent) curEvent);
break;
+ case BLOCKING_STATE:
+ onBlockingState((BlockingTransitionInternalEvent) curEvent);
+ break;
case SUBSCRIPTION_TRANSITION:
// We only dispatch the event for the effective date and not the requested date since we have both
// for subscription events.
@@ -145,6 +149,11 @@ public class CompletionUserRequestBase implements CompletionUserRequest {
}
@Override
+ public void onBlockingState(final BlockingTransitionInternalEvent curEvent) {
+
+ }
+
+ @Override
public void onInvoiceCreation(final InvoiceCreationInternalEvent curEvent) {
}
diff --git a/util/src/main/java/org/killbill/billing/util/userrequest/CompletionUserRequestWaiter.java b/util/src/main/java/org/killbill/billing/util/userrequest/CompletionUserRequestWaiter.java
index 6668dcf..294149c 100644
--- a/util/src/main/java/org/killbill/billing/util/userrequest/CompletionUserRequestWaiter.java
+++ b/util/src/main/java/org/killbill/billing/util/userrequest/CompletionUserRequestWaiter.java
@@ -21,6 +21,7 @@ import java.util.concurrent.TimeoutException;
import org.killbill.billing.events.AccountChangeInternalEvent;
import org.killbill.billing.events.AccountCreationInternalEvent;
+import org.killbill.billing.events.BlockingTransitionInternalEvent;
import org.killbill.billing.events.BusInternalEvent;
import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
import org.killbill.billing.events.InvoiceCreationInternalEvent;
@@ -37,7 +38,9 @@ public interface CompletionUserRequestWaiter {
public void onAccountChange(final AccountChangeInternalEvent curEvent);
- public void onSubscriptionBaseTransition(final EffectiveSubscriptionInternalEvent curEventEffective);
+ public void onSubscriptionBaseTransition(final EffectiveSubscriptionInternalEvent curEvent);
+
+ public void onBlockingState(final BlockingTransitionInternalEvent curEvent);
public void onInvoiceCreation(final InvoiceCreationInternalEvent curEvent);
diff --git a/util/src/test/java/org/killbill/billing/api/TestApiListener.java b/util/src/test/java/org/killbill/billing/api/TestApiListener.java
index 690707a..cbe643b 100644
--- a/util/src/test/java/org/killbill/billing/api/TestApiListener.java
+++ b/util/src/test/java/org/killbill/billing/api/TestApiListener.java
@@ -30,13 +30,13 @@ import javax.inject.Inject;
import org.killbill.billing.events.BlockingTransitionInternalEvent;
import org.killbill.billing.events.BroadcastInternalEvent;
import org.killbill.billing.events.CustomFieldEvent;
-import org.killbill.billing.events.EffectiveEntitlementInternalEvent;
import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
import org.killbill.billing.events.InvoiceAdjustmentInternalEvent;
import org.killbill.billing.events.InvoiceCreationInternalEvent;
import org.killbill.billing.events.InvoiceNotificationInternalEvent;
import org.killbill.billing.events.InvoicePaymentErrorInternalEvent;
import org.killbill.billing.events.InvoicePaymentInfoInternalEvent;
+import org.killbill.billing.events.NullInvoiceInternalEvent;
import org.killbill.billing.events.PaymentErrorInternalEvent;
import org.killbill.billing.events.PaymentInfoInternalEvent;
import org.killbill.billing.events.PaymentPluginErrorInternalEvent;
@@ -100,12 +100,9 @@ public class TestApiListener {
}
public enum NextEvent {
- MIGRATE_ENTITLEMENT,
- MIGRATE_BILLING,
BROADCAST_SERVICE,
CREATE,
TRANSFER,
- RE_CREATE,
CHANGE,
CANCEL,
UNCANCEL,
@@ -113,6 +110,7 @@ public class TestApiListener {
RESUME,
PHASE,
BLOCK,
+ NULL_INVOICE,
INVOICE,
INVOICE_NOTIFICATION,
INVOICE_ADJUSTMENT,
@@ -127,7 +125,6 @@ public class TestApiListener {
CUSTOM_FIELD,
}
-
@Subscribe
public void handleBroadcastEvents(final BroadcastInternalEvent event) {
log.info(String.format("Got BroadcastInternalEvent event %s", event.toString()));
@@ -135,7 +132,6 @@ public class TestApiListener {
notifyIfStackEmpty();
}
-
@Subscribe
public void handleRepairSubscriptionEvents(final RepairSubscriptionInternalEvent event) {
log.info(String.format("Got RepairSubscriptionEvent event %s", event.toString()));
@@ -144,21 +140,6 @@ public class TestApiListener {
}
@Subscribe
- public void handleEntitlementEvents(final EffectiveEntitlementInternalEvent eventEffective) {
- log.info(String.format("Got entitlement event %s", eventEffective.toString()));
- switch (eventEffective.getTransitionType()) {
- case BLOCK_BUNDLE:
- assertEqualsNicely(NextEvent.PAUSE);
- notifyIfStackEmpty();
- break;
- case UNBLOCK_BUNDLE:
- assertEqualsNicely(NextEvent.RESUME);
- notifyIfStackEmpty();
- break;
- }
- }
-
- @Subscribe
public void handleEntitlementEvents(final BlockingTransitionInternalEvent event) {
log.info(String.format("Got entitlement event %s", event.toString()));
assertEqualsNicely(NextEvent.BLOCK);
@@ -173,22 +154,10 @@ public class TestApiListener {
assertEqualsNicely(NextEvent.TRANSFER);
notifyIfStackEmpty();
break;
- case MIGRATE_ENTITLEMENT:
- assertEqualsNicely(NextEvent.MIGRATE_ENTITLEMENT);
- notifyIfStackEmpty();
- break;
- case MIGRATE_BILLING:
- assertEqualsNicely(NextEvent.MIGRATE_BILLING);
- notifyIfStackEmpty();
- break;
case CREATE:
assertEqualsNicely(NextEvent.CREATE);
notifyIfStackEmpty();
break;
- case RE_CREATE:
- assertEqualsNicely(NextEvent.RE_CREATE);
- notifyIfStackEmpty();
- break;
case CANCEL:
assertEqualsNicely(NextEvent.CANCEL);
notifyIfStackEmpty();
@@ -239,6 +208,13 @@ public class TestApiListener {
}
@Subscribe
+ public void handleNullInvoiceEvents(final NullInvoiceInternalEvent event) {
+ log.info(String.format("Got Null Invoice event %s", event.toString()));
+ assertEqualsNicely(NextEvent.NULL_INVOICE);
+ notifyIfStackEmpty();
+ }
+
+ @Subscribe
public void handleInvoiceEvents(final InvoiceCreationInternalEvent event) {
log.info(String.format("Got Invoice event %s", event.toString()));
assertEqualsNicely(NextEvent.INVOICE);
diff --git a/util/src/test/java/org/killbill/billing/callcontext/MutableInternalCallContext.java b/util/src/test/java/org/killbill/billing/callcontext/MutableInternalCallContext.java
new file mode 100644
index 0000000..2066bcf
--- /dev/null
+++ b/util/src/test/java/org/killbill/billing/callcontext/MutableInternalCallContext.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2016 Groupon, Inc
+ * Copyright 2016 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.callcontext;
+
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalTime;
+import org.killbill.billing.util.callcontext.CallOrigin;
+import org.killbill.billing.util.callcontext.UserType;
+
+public class MutableInternalCallContext extends InternalCallContext {
+
+ private final Long initialAccountRecordId;
+ private final Long initialTenantRecordId;
+ private final DateTimeZone initialReferenceDateTimeZone;
+ private final LocalTime initialReferenceTime;
+ private final DateTime initialCreatedDate;
+ private final DateTime initialUpdatedDate;
+
+ private Long accountRecordId;
+ private Long tenantRecordId;
+ private DateTimeZone fixedOffsetTimeZone;
+ private LocalTime referenceTime;
+ private DateTime createdDate;
+ private DateTime updatedDate;
+
+ public MutableInternalCallContext(final Long tenantRecordId,
+ @Nullable final Long accountRecordId,
+ @Nullable final DateTimeZone fixedOffsetTimeZone,
+ @Nullable final DateTime referenceTime,
+ final UUID userToken,
+ final String userName,
+ final CallOrigin callOrigin,
+ final UserType userType,
+ final String reasonCode,
+ final String comment,
+ final DateTime createdDate,
+ final DateTime updatedDate) {
+ super(tenantRecordId, accountRecordId, fixedOffsetTimeZone, referenceTime, userToken, userName, callOrigin, userType, reasonCode, comment, createdDate, updatedDate);
+ this.initialAccountRecordId = accountRecordId;
+ this.initialTenantRecordId = tenantRecordId;
+ this.initialReferenceDateTimeZone = fixedOffsetTimeZone;
+ this.initialReferenceTime = super.getReferenceTime();
+ this.initialCreatedDate = createdDate;
+ this.initialUpdatedDate = updatedDate;
+
+ reset();
+ }
+
+ @Override
+ public Long getAccountRecordId() {
+ return accountRecordId;
+ }
+
+ public void setAccountRecordId(final Long accountRecordId) {
+ this.accountRecordId = accountRecordId;
+ }
+
+ @Override
+ public Long getTenantRecordId() {
+ return tenantRecordId;
+ }
+
+ public void setTenantRecordId(final Long tenantRecordId) {
+ this.tenantRecordId = tenantRecordId;
+ }
+
+ @Override
+ public DateTimeZone getFixedOffsetTimeZone() {
+ return fixedOffsetTimeZone;
+ }
+
+ public void setFixedOffsetTimeZone(final DateTimeZone fixedOffsetTimeZone) {
+ this.fixedOffsetTimeZone = fixedOffsetTimeZone;
+ }
+
+ @Override
+ public LocalTime getReferenceTime() {
+ return referenceTime;
+ }
+
+ public void setReferenceTime(final LocalTime referenceTime) {
+ this.referenceTime = referenceTime;
+ }
+
+ public void setReferenceTime(final DateTime referenceDateTime) {
+ this.referenceTime = computeReferenceTime(referenceDateTime);
+ }
+
+ @Override
+ public DateTime getCreatedDate() {
+ return createdDate;
+ }
+
+ public void setCreatedDate(final DateTime createdDate) {
+ this.createdDate = createdDate;
+ }
+
+ @Override
+ public DateTime getUpdatedDate() {
+ return updatedDate;
+ }
+
+ public void setUpdatedDate(final DateTime updatedDate) {
+ this.updatedDate = updatedDate;
+ }
+
+ public void reset() {
+ setAccountRecordId(initialAccountRecordId);
+ setTenantRecordId(initialTenantRecordId);
+ setFixedOffsetTimeZone(initialReferenceDateTimeZone);
+ setReferenceTime(initialReferenceTime);
+ setCreatedDate(initialCreatedDate);
+ setUpdatedDate(initialUpdatedDate);
+ }
+}
diff --git a/util/src/test/java/org/killbill/billing/GuicyKillbillTestModule.java b/util/src/test/java/org/killbill/billing/GuicyKillbillTestModule.java
index 7cda860..3aea65b 100644
--- a/util/src/test/java/org/killbill/billing/GuicyKillbillTestModule.java
+++ b/util/src/test/java/org/killbill/billing/GuicyKillbillTestModule.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -20,7 +20,9 @@ package org.killbill.billing;
import java.util.UUID;
+import org.joda.time.DateTimeZone;
import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.MutableInternalCallContext;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.CallOrigin;
@@ -37,10 +39,18 @@ public class GuicyKillbillTestModule extends KillBillModule {
// That we we have only one clock and all internalContext/callcontext are consistent
//
- private final InternalCallContext internalCallContext = new InternalCallContext(InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID, 1687L, UUID.randomUUID(),
- UUID.randomUUID().toString(), CallOrigin.TEST,
- UserType.TEST, "Testing", "This is a test",
- GuicyKillbillTestSuite.getClock().getUTCNow(), GuicyKillbillTestSuite.getClock().getUTCNow());
+ private final MutableInternalCallContext internalCallContext = new MutableInternalCallContext(InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID,
+ 1687L,
+ DateTimeZone.UTC,
+ GuicyKillbillTestSuite.getClock().getUTCNow(),
+ UUID.randomUUID(),
+ UUID.randomUUID().toString(),
+ CallOrigin.TEST,
+ UserType.TEST,
+ "Testing",
+ "This is a test",
+ GuicyKillbillTestSuite.getClock().getUTCNow(),
+ GuicyKillbillTestSuite.getClock().getUTCNow());
private final CallContext callContext = internalCallContext.toCallContext(null);
@@ -53,6 +63,7 @@ public class GuicyKillbillTestModule extends KillBillModule {
bind(ClockMock.class).toInstance(GuicyKillbillTestSuite.getClock());
bind(Clock.class).to(ClockMock.class);
bind(InternalCallContext.class).toInstance(internalCallContext);
+ bind(MutableInternalCallContext.class).toInstance(internalCallContext);
bind(CallContext.class).toInstance(callContext);
}
}
diff --git a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuite.java b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuite.java
index 7debdc2..badf00a 100644
--- a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuite.java
+++ b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuite.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -19,13 +19,18 @@
package org.killbill.billing;
import java.lang.reflect.Method;
+import java.util.UUID;
import javax.inject.Inject;
-import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.callcontext.MutableInternalCallContext;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.platform.test.config.TestKillbillConfigSource;
import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.callcontext.TenantContext;
+import org.killbill.clock.Clock;
import org.killbill.clock.ClockMock;
import org.skife.config.ConfigSource;
import org.slf4j.Logger;
@@ -44,7 +49,10 @@ public class GuicyKillbillTestSuite {
private boolean hasFailed = false;
@Inject
- protected InternalCallContext internalCallContext;
+ protected InternalCallContextFactory internalCallContextFactory;
+
+ @Inject
+ protected MutableInternalCallContext internalCallContext;
@Inject
protected CallContext callContext;
@@ -95,11 +103,30 @@ public class GuicyKillbillTestSuite {
return theStaticClock;
}
+ public static void refreshCallContext(final UUID accountId,
+ final Clock clock,
+ final InternalCallContextFactory internalCallContextFactory,
+ final TenantContext callContext,
+ final MutableInternalCallContext internalCallContext) {
+ final InternalTenantContext tmp = internalCallContextFactory.createInternalTenantContext(accountId, callContext);
+ internalCallContext.setAccountRecordId(tmp.getAccountRecordId());
+ internalCallContext.setFixedOffsetTimeZone(tmp.getFixedOffsetTimeZone());
+ internalCallContext.setReferenceTime(tmp.getReferenceTime());
+ internalCallContext.setCreatedDate(clock.getUTCNow());
+ internalCallContext.setUpdatedDate(clock.getUTCNow());
+ }
+
+ protected void refreshCallContext(final UUID accountId) {
+ refreshCallContext(accountId, clock, internalCallContextFactory, callContext, internalCallContext);
+ }
+
@BeforeMethod(alwaysRun = true)
public void beforeMethodAlwaysRun(final Method method) throws Exception {
log.info("***************************************************************************************************");
log.info("*** Starting test {}:{}", method.getDeclaringClass().getName(), method.getName());
log.info("***************************************************************************************************");
+
+ internalCallContext.reset();
}
@AfterMethod(alwaysRun = true)
diff --git a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteNoDB.java b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteNoDB.java
index 734aaef..cc2e479 100644
--- a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteNoDB.java
+++ b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteNoDB.java
@@ -16,7 +16,45 @@
package org.killbill.billing;
-public class GuicyKillbillTestSuiteNoDB extends GuicyKillbillTestSuite {
+import java.util.UUID;
+import org.killbill.billing.account.api.Account;
+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.ImmutableAccountInternalApi;
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.callcontext.MutableInternalCallContext;
+import org.killbill.billing.dao.MockNonEntityDao;
+import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.dao.NonEntityDao;
+import org.killbill.clock.Clock;
+import org.mockito.Mockito;
+public class GuicyKillbillTestSuiteNoDB extends GuicyKillbillTestSuite {
+
+ public static Account createMockAccount(final Account accountData,
+ final AccountUserApi accountUserApi,
+ final AccountInternalApi accountInternalApi,
+ final ImmutableAccountInternalApi immutableAccountInternalApi,
+ final NonEntityDao nonEntityDao,
+ final Clock clock,
+ final InternalCallContextFactory internalCallContextFactory,
+ final CallContext callContext,
+ final MutableInternalCallContext internalCallContext) throws AccountApiException {
+ final Account account = accountUserApi.createAccount(accountData, callContext);
+
+ Mockito.when(accountInternalApi.getAccountById(Mockito.<UUID>eq(account.getId()), Mockito.<InternalTenantContext>any())).thenReturn(account);
+ Mockito.when(accountInternalApi.getAccountByRecordId(Mockito.<Long>eq(internalCallContext.getAccountRecordId()), Mockito.<InternalTenantContext>any())).thenReturn(account);
+ Mockito.when(accountInternalApi.getAccountByKey(Mockito.<String>eq(account.getExternalKey()), Mockito.<InternalTenantContext>any())).thenReturn(account);
+ Mockito.when(immutableAccountInternalApi.getImmutableAccountDataByRecordId(Mockito.<Long>eq(internalCallContext.getAccountRecordId()), Mockito.<InternalTenantContext>any())).thenReturn(account);
+
+ ((MockNonEntityDao) nonEntityDao).addTenantRecordIdMapping(account.getId(), internalCallContext);
+ ((MockNonEntityDao) nonEntityDao).addAccountRecordIdMapping(account.getId(), internalCallContext);
+
+ refreshCallContext(account.getId(), clock, internalCallContextFactory, callContext, internalCallContext);
+
+ return account;
+ }
}
diff --git a/util/src/test/java/org/killbill/billing/KillbillTestSuite.java b/util/src/test/java/org/killbill/billing/KillbillTestSuite.java
index 7af49d2..ebb0d5b 100644
--- a/util/src/test/java/org/killbill/billing/KillbillTestSuite.java
+++ b/util/src/test/java/org/killbill/billing/KillbillTestSuite.java
@@ -1,7 +1,9 @@
/*
- * Copyright 2010-2012 Ning, Inc.
+ * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -17,22 +19,15 @@
package org.killbill.billing;
import java.lang.reflect.Method;
-import java.util.UUID;
+import org.killbill.clock.Clock;
+import org.killbill.clock.ClockMock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.ITestResult;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
-import org.killbill.billing.util.callcontext.CallContext;
-import org.killbill.billing.util.callcontext.CallOrigin;
-import org.killbill.billing.callcontext.InternalCallContext;
-import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.util.callcontext.UserType;
-import org.killbill.clock.Clock;
-import org.killbill.clock.ClockMock;
-
public class KillbillTestSuite {
// Use the simple name here to save screen real estate
@@ -42,12 +37,6 @@ public class KillbillTestSuite {
protected Clock clock = new ClockMock();
- protected final InternalCallContext internalCallContext = new InternalCallContext(InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID, 1687L, UUID.randomUUID(),
- UUID.randomUUID().toString(), CallOrigin.TEST,
- UserType.TEST, "Testing", "This is a test",
- clock.getUTCNow(), clock.getUTCNow());
- protected final CallContext callContext = internalCallContext.toCallContext(null);
-
@BeforeMethod(alwaysRun = true)
public void startTestSuite(final Method method) throws Exception {
log.info("***************************************************************************************************");
diff --git a/util/src/test/java/org/killbill/billing/mock/api/MockAccountUserApi.java b/util/src/test/java/org/killbill/billing/mock/api/MockAccountUserApi.java
index fc586f2..38f93a1 100644
--- a/util/src/test/java/org/killbill/billing/mock/api/MockAccountUserApi.java
+++ b/util/src/test/java/org/killbill/billing/mock/api/MockAccountUserApi.java
@@ -16,10 +16,11 @@
package org.killbill.billing.mock.api;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
-import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ConcurrentLinkedQueue;
import org.joda.time.DateTimeZone;
import org.killbill.billing.account.api.Account;
@@ -33,10 +34,11 @@ import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.TenantContext;
import org.killbill.billing.util.entity.DefaultPagination;
import org.killbill.billing.util.entity.Pagination;
+import org.testng.Assert;
public class MockAccountUserApi implements AccountUserApi {
- private final CopyOnWriteArrayList<Account> accounts = new CopyOnWriteArrayList<Account>();
+ private final ConcurrentLinkedQueue<Account> accounts = new ConcurrentLinkedQueue<Account>();
public Account createAccountFromParams(final UUID id,
final String externalKey,
@@ -152,7 +154,19 @@ public class MockAccountUserApi implements AccountUserApi {
@Override
public void updateAccount(final Account account, final CallContext context) {
- throw new UnsupportedOperationException();
+ final Iterator<Account> iterator = accounts.iterator();
+ while (iterator.hasNext()) {
+ final Account account1 = iterator.next();
+ if (account.getId().equals(account1.getId())) {
+ iterator.remove();
+ break;
+ }
+ }
+ try {
+ createAccount(account, context);
+ } catch (final AccountApiException e) {
+ Assert.fail(e.toString());
+ }
}
@Override
diff --git a/util/src/test/java/org/killbill/billing/mock/glue/MockAccountModule.java b/util/src/test/java/org/killbill/billing/mock/glue/MockAccountModule.java
index 5efae87..a96e1b0 100644
--- a/util/src/test/java/org/killbill/billing/mock/glue/MockAccountModule.java
+++ b/util/src/test/java/org/killbill/billing/mock/glue/MockAccountModule.java
@@ -18,9 +18,13 @@
package org.killbill.billing.mock.glue;
+import org.joda.time.DateTimeZone;
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.account.api.ImmutableAccountInternalApi;
import org.killbill.billing.glue.AccountModule;
+import org.killbill.billing.mock.api.MockAccountUserApi;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.util.glue.KillBillModule;
import org.mockito.Mockito;
@@ -39,11 +43,18 @@ public class MockAccountModule extends KillBillModule implements AccountModule {
@Override
public void installAccountUserApi() {
- bind(AccountUserApi.class).toInstance(Mockito.mock(AccountUserApi.class));
+ bind(AccountUserApi.class).toInstance(new MockAccountUserApi());
}
@Override
public void installInternalApi() {
- bind(AccountInternalApi.class).toInstance(Mockito.mock(AccountInternalApi.class));
+ final ImmutableAccountData immutableAccountData = Mockito.mock(ImmutableAccountData.class);
+ Mockito.when(immutableAccountData.getTimeZone()).thenReturn(DateTimeZone.UTC);
+ Mockito.when(immutableAccountData.getFixedOffsetTimeZone()).thenReturn(DateTimeZone.UTC);
+
+ final AccountInternalApi accountInternalApi = Mockito.mock(AccountInternalApi.class);
+ final ImmutableAccountInternalApi immutableAccountInternalApi = Mockito.mock(ImmutableAccountInternalApi.class);
+ bind(AccountInternalApi.class).toInstance(accountInternalApi);
+ bind(ImmutableAccountInternalApi.class).toInstance(immutableAccountInternalApi);
}
}
diff --git a/util/src/test/java/org/killbill/billing/mock/glue/MockInvoiceModule.java b/util/src/test/java/org/killbill/billing/mock/glue/MockInvoiceModule.java
index 755817c..3824d12 100644
--- a/util/src/test/java/org/killbill/billing/mock/glue/MockInvoiceModule.java
+++ b/util/src/test/java/org/killbill/billing/mock/glue/MockInvoiceModule.java
@@ -20,7 +20,6 @@ package org.killbill.billing.mock.glue;
import org.killbill.billing.glue.InvoiceModule;
import org.killbill.billing.invoice.api.InvoiceInternalApi;
-import org.killbill.billing.invoice.api.InvoiceMigrationApi;
import org.killbill.billing.invoice.api.InvoicePaymentApi;
import org.killbill.billing.invoice.api.InvoiceUserApi;
import org.killbill.billing.platform.api.KillbillConfigSource;
@@ -44,16 +43,10 @@ public class MockInvoiceModule extends KillBillModule implements InvoiceModule {
}
@Override
- public void installInvoiceMigrationApi() {
- bind(InvoiceMigrationApi.class).toInstance(Mockito.mock(InvoiceMigrationApi.class));
- }
-
- @Override
protected void configure() {
installInvoiceUserApi();
installInvoiceInternalApi();
installInvoicePaymentApi();
- installInvoiceMigrationApi();
}
@Override
diff --git a/util/src/test/java/org/killbill/billing/mock/glue/MockSubscriptionModule.java b/util/src/test/java/org/killbill/billing/mock/glue/MockSubscriptionModule.java
index 78bab07..7f6a1d8 100644
--- a/util/src/test/java/org/killbill/billing/mock/glue/MockSubscriptionModule.java
+++ b/util/src/test/java/org/killbill/billing/mock/glue/MockSubscriptionModule.java
@@ -22,7 +22,6 @@ import org.killbill.billing.glue.SubscriptionModule;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
import org.killbill.billing.subscription.api.SubscriptionBaseService;
-import org.killbill.billing.subscription.api.migration.SubscriptionBaseMigrationApi;
import org.killbill.billing.subscription.api.timeline.SubscriptionBaseTimelineApi;
import org.killbill.billing.subscription.api.transfer.SubscriptionBaseTransferApi;
import org.killbill.billing.util.glue.KillBillModule;
@@ -39,10 +38,6 @@ public class MockSubscriptionModule extends KillBillModule implements Subscripti
bind(SubscriptionBaseService.class).toInstance(Mockito.mock(SubscriptionBaseService.class));
}
- @Override
- public void installSubscriptionMigrationApi() {
- bind(SubscriptionBaseMigrationApi.class).toInstance(Mockito.mock(SubscriptionBaseMigrationApi.class));
- }
@Override
public void installSubscriptionInternalApi() {
@@ -52,7 +47,6 @@ public class MockSubscriptionModule extends KillBillModule implements Subscripti
@Override
protected void configure() {
installSubscriptionService();
- installSubscriptionMigrationApi();
installSubscriptionInternalApi();
installSubscriptionTimelineApi();
installSubscriptionTransferApi();
diff --git a/util/src/test/java/org/killbill/billing/mock/MockAccountBuilder.java b/util/src/test/java/org/killbill/billing/mock/MockAccountBuilder.java
index bee5f0b..3058afa 100644
--- a/util/src/test/java/org/killbill/billing/mock/MockAccountBuilder.java
+++ b/util/src/test/java/org/killbill/billing/mock/MockAccountBuilder.java
@@ -24,6 +24,7 @@ import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountData;
import org.killbill.billing.account.api.MutableAccountData;
import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.util.account.AccountDateTimeUtils;
public class MockAccountBuilder {
@@ -61,7 +62,6 @@ public class MockAccountBuilder {
}
public MockAccountBuilder(final AccountData data) {
- this.id = UUID.randomUUID();
this.address1(data.getAddress1());
this.address2(data.getAddress2());
this.billingCycleDayLocal(data.getBillCycleDayLocal());
@@ -83,6 +83,13 @@ public class MockAccountBuilder {
this.postalCode(data.getPostalCode());
this.stateOrProvince(data.getStateOrProvince());
this.timeZone(data.getTimeZone());
+ if (data instanceof Account) {
+ this.id = ((Account) data).getId();
+ this.createdDate(((Account) data).getCreatedDate());
+ this.updatedDate(((Account) data).getUpdatedDate());
+ } else {
+ this.id = UUID.randomUUID();
+ }
}
public MockAccountBuilder externalKey(final String externalKey) {
@@ -219,109 +226,101 @@ public class MockAccountBuilder {
@Override
public String getName() {
-
return name;
}
@Override
public Integer getFirstNameLength() {
-
return firstNameLength;
}
@Override
public String getEmail() {
-
return email;
}
@Override
public Integer getBillCycleDayLocal() {
-
return billingCycleDayLocal;
}
@Override
public Currency getCurrency() {
-
return currency;
}
@Override
public UUID getPaymentMethodId() {
-
return paymentMethodId;
}
@Override
public DateTimeZone getTimeZone() {
-
return timeZone;
}
@Override
- public String getLocale() {
+ public DateTimeZone getFixedOffsetTimeZone() {
+ return AccountDateTimeUtils.getFixedOffsetTimeZone(this);
+ }
+
+ @Override
+ public DateTime getReferenceTime() {
+ return AccountDateTimeUtils.getReferenceDateTime(this);
+ }
+ @Override
+ public String getLocale() {
return locale;
}
@Override
public String getAddress1() {
-
return address1;
}
@Override
public String getAddress2() {
-
return address2;
}
@Override
public String getCompanyName() {
-
return companyName;
}
@Override
public String getCity() {
-
return city;
}
@Override
public String getStateOrProvince() {
-
return stateOrProvince;
}
@Override
public String getPostalCode() {
-
return postalCode;
}
@Override
public String getCountry() {
-
return country;
}
@Override
public String getPhone() {
-
return phone;
}
@Override
public Boolean isMigrated() {
-
return migrated;
}
@Override
public Boolean isNotifiedForInvoices() {
-
return isNotifiedForInvoices;
}
diff --git a/util/src/test/java/org/killbill/billing/mock/MockPlan.java b/util/src/test/java/org/killbill/billing/mock/MockPlan.java
index 0b5e92d..004346e 100644
--- a/util/src/test/java/org/killbill/billing/mock/MockPlan.java
+++ b/util/src/test/java/org/killbill/billing/mock/MockPlan.java
@@ -26,6 +26,7 @@ import org.killbill.billing.catalog.api.CatalogApiException;
import org.killbill.billing.catalog.api.PhaseType;
import org.killbill.billing.catalog.api.Plan;
import org.killbill.billing.catalog.api.PlanPhase;
+import org.killbill.billing.catalog.api.PriceList;
import org.killbill.billing.catalog.api.Product;
public class MockPlan implements Plan {
@@ -52,6 +53,11 @@ public class MockPlan implements Plan {
}
@Override
+ public PriceList getPriceList() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public String getName() {
return name;
}
diff --git a/util/src/test/java/org/killbill/billing/mock/MockSubscription.java b/util/src/test/java/org/killbill/billing/mock/MockSubscription.java
index 8dd38d7..f852a1b 100644
--- a/util/src/test/java/org/killbill/billing/mock/MockSubscription.java
+++ b/util/src/test/java/org/killbill/billing/mock/MockSubscription.java
@@ -167,6 +167,11 @@ public class MockSubscription implements SubscriptionBase {
}
@Override
+ public boolean isMigrated() {
+ return false;
+ }
+
+ @Override
public ProductCategory getCategory() {
return sub.getCategory();
}
diff --git a/util/src/test/java/org/killbill/billing/util/cache/TestCache.java b/util/src/test/java/org/killbill/billing/util/cache/TestCache.java
index 98b84d9..d24810c 100644
--- a/util/src/test/java/org/killbill/billing/util/cache/TestCache.java
+++ b/util/src/test/java/org/killbill/billing/util/cache/TestCache.java
@@ -60,7 +60,7 @@ public class TestCache extends UtilTestSuiteWithEmbeddedDB {
@Test(groups = "slow")
public void testCacheRecordId() throws Exception {
- this.transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, controlCacheDispatcher, nonEntityDao);
+ this.transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, controlCacheDispatcher, nonEntityDao, internalCallContextFactory);
final TagModelDao tag = new TagModelDao(clock.getUTCNow(), UUID.randomUUID(), UUID.randomUUID(), ObjectType.TAG);
// Verify we start with nothing in the cache
@@ -88,7 +88,7 @@ public class TestCache extends UtilTestSuiteWithEmbeddedDB {
@Test(groups = "slow")
public void testAllCachesAfterGetById() throws Exception {
- this.transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, controlCacheDispatcher, nonEntityDao);
+ this.transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, controlCacheDispatcher, nonEntityDao, internalCallContextFactory);
final TagModelDao tag = new TagModelDao(clock.getUTCNow(), UUID.randomUUID(), UUID.randomUUID(), ObjectType.TAG);
insertTag(tag);
diff --git a/util/src/test/java/org/killbill/billing/util/callcontext/TestCallContext.java b/util/src/test/java/org/killbill/billing/util/callcontext/TestCallContext.java
index 5798beb..43a57cf 100644
--- a/util/src/test/java/org/killbill/billing/util/callcontext/TestCallContext.java
+++ b/util/src/test/java/org/killbill/billing/util/callcontext/TestCallContext.java
@@ -1,7 +1,9 @@
/*
- * Copyright 2010-2011 Ning, Inc.
+ * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -34,6 +36,14 @@ public class TestCallContext implements CallContext {
this(userName, new DefaultClock().getUTCNow(), new DefaultClock().getUTCNow());
}
+ public TestCallContext(final CallContext context, final DateTime utcNow) {
+ this.userName = context.getUserName();
+ this.createdDate = utcNow;
+ this.updatedDate = utcNow;
+ this.userToken = context.getUserToken();
+ this.tenantId = context.getTenantId();
+ }
+
public TestCallContext(final String userName, final DateTime createdDate, final DateTime updatedDate) {
this.userName = userName;
this.createdDate = createdDate;
diff --git a/util/src/test/java/org/killbill/billing/util/callcontext/TestInternalCallContextFactory.java b/util/src/test/java/org/killbill/billing/util/callcontext/TestInternalCallContextFactory.java
index 311c7d6..9a5c01e 100644
--- a/util/src/test/java/org/killbill/billing/util/callcontext/TestInternalCallContextFactory.java
+++ b/util/src/test/java/org/killbill/billing/util/callcontext/TestInternalCallContextFactory.java
@@ -90,9 +90,9 @@ public class TestInternalCallContextFactory extends UtilTestSuiteWithEmbeddedDB
private void verifyInternalCallContext(final InternalCallContext context) {
Assert.assertEquals(context.getCallOrigin(), callContext.getCallOrigin());
Assert.assertEquals(context.getComments(), callContext.getComments());
- Assert.assertEquals(context.getCreatedDate(), callContext.getCreatedDate());
+ Assert.assertTrue(context.getCreatedDate().compareTo(callContext.getCreatedDate()) >= 0);
Assert.assertEquals(context.getReasonCode(), callContext.getReasonCode());
- Assert.assertEquals(context.getUpdatedDate(), callContext.getUpdatedDate());
+ Assert.assertTrue(context.getUpdatedDate().compareTo(callContext.getUpdatedDate()) >= 0);
Assert.assertEquals(context.getCreatedBy(), callContext.getUserName());
Assert.assertEquals(context.getUserToken(), callContext.getUserToken());
Assert.assertEquals(context.getContextUserType(), callContext.getUserType());
diff --git a/util/src/test/java/org/killbill/billing/util/callcontext/TestTimeAwareContext.java b/util/src/test/java/org/killbill/billing/util/callcontext/TestTimeAwareContext.java
new file mode 100644
index 0000000..49ecb00
--- /dev/null
+++ b/util/src/test/java/org/killbill/billing/util/callcontext/TestTimeAwareContext.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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.callcontext;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.callcontext.TimeAwareContext;
+import org.killbill.billing.mock.MockAccountBuilder;
+import org.killbill.billing.util.UtilTestSuiteNoDB;
+import org.killbill.billing.util.account.AccountDateTimeUtils;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+//
+// There are two categories of tests, one that test the offset calculation and one that calculates
+// how to get a DateTime from a LocalDate (in account time zone)
+//
+// Tests {1, 2, 3} use an account timezone with a negative offset (-8) and tests {A, B, C} use an account timezone with a positive offset (+8)
+//
+public class TestTimeAwareContext extends UtilTestSuiteNoDB {
+
+ private final DateTimeFormatter DATE_TIME_FORMATTER = ISODateTimeFormat.dateTimeParser();
+ private final String effectiveDateTime1 = "2012-01-20T07:30:42.000Z";
+ private final String effectiveDateTime2 = "2012-01-20T08:00:00.000Z";
+ private final String effectiveDateTime3 = "2012-01-20T08:45:33.000Z";
+ private final String effectiveDateTimeA = "2012-01-20T16:30:42.000Z";
+ private final String effectiveDateTimeB = "2012-01-20T16:00:00.000Z";
+ private final String effectiveDateTimeC = "2012-01-20T15:30:42.000Z";
+
+ @Test(groups = "fast")
+ public void testComputeUTCDateTimeFromLocalDate1() {
+ final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime1);
+ final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8);
+ refreshCallContext(effectiveDateTime, timeZone);
+
+ final LocalDate endDate = new LocalDate(2013, 01, 19);
+ final DateTime endDateTimeInUTC = internalCallContext.toUTCDateTime(endDate);
+ assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0);
+ }
+
+ @Test(groups = "fast")
+ public void testComputeUTCDateTimeFromLocalDate2() {
+ final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime2);
+ final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8);
+ refreshCallContext(effectiveDateTime, timeZone);
+
+ final LocalDate endDate = new LocalDate(2013, 01, 20);
+ final DateTime endDateTimeInUTC = internalCallContext.toUTCDateTime(endDate);
+ assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0);
+ }
+
+ @Test(groups = "fast")
+ public void testComputeUTCDateTimeFromLocalDate3() {
+ final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime3);
+ final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8);
+ refreshCallContext(effectiveDateTime, timeZone);
+
+ final LocalDate endDate = new LocalDate(2013, 01, 20);
+ final DateTime endDateTimeInUTC = internalCallContext.toUTCDateTime(endDate);
+ assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0);
+ }
+
+ @Test(groups = "fast")
+ public void testComputeUTCDateTimeFromLocalDateA() {
+ final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeA);
+ final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8);
+ refreshCallContext(effectiveDateTime, timeZone);
+
+ final LocalDate endDate = new LocalDate(2013, 01, 21);
+ final DateTime endDateTimeInUTC = internalCallContext.toUTCDateTime(endDate);
+ assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0);
+ }
+
+ @Test(groups = "fast")
+ public void testComputeUTCDateTimeFromLocalDateB() {
+ final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeB);
+ final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8);
+ refreshCallContext(effectiveDateTime, timeZone);
+
+ final LocalDate endDate = new LocalDate(2013, 01, 21);
+ final DateTime endDateTimeInUTC = internalCallContext.toUTCDateTime(endDate);
+ assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0);
+ }
+
+ @Test(groups = "fast")
+ public void testComputeUTCDateTimeFromLocalDateC() {
+ final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeC);
+ final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8);
+ refreshCallContext(effectiveDateTime, timeZone);
+
+ final LocalDate endDate = new LocalDate(2013, 01, 20);
+ final DateTime endDateTimeInUTC = internalCallContext.toUTCDateTime(endDate);
+ assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0);
+ }
+
+ @Test(groups = "fast")
+ public void testComputeTargetDateWithDayLightSaving() {
+ final DateTime dateTime1 = new DateTime("2015-01-01T08:01:01.000Z");
+ final DateTime dateTime2 = new DateTime("2015-09-01T08:01:01.000Z");
+ final DateTime dateTime3 = new DateTime("2015-12-01T08:01:01.000Z");
+
+ // Alaska Standard Time
+ final DateTimeZone timeZone = DateTimeZone.forID("America/Juneau");
+
+ // Time zone is AKDT (UTC-8h) between March and November
+ final DateTime referenceDateTimeWithDST = new DateTime("2015-09-01T08:01:01.000Z");
+ refreshCallContext(referenceDateTimeWithDST, timeZone);
+
+ assertEquals(internalCallContext.toLocalDate(dateTime1), new LocalDate("2015-01-01"));
+ assertEquals(internalCallContext.toLocalDate(dateTime2), new LocalDate("2015-09-01"));
+ assertEquals(internalCallContext.toLocalDate(dateTime3), new LocalDate("2015-12-01"));
+
+ // Time zone is AKST (UTC-9h) otherwise
+ final DateTime referenceDateTimeWithoutDST = new DateTime("2015-02-01T08:01:01.000Z");
+ refreshCallContext(referenceDateTimeWithoutDST, timeZone);
+
+ assertEquals(internalCallContext.toLocalDate(dateTime1), new LocalDate("2014-12-31"));
+ assertEquals(internalCallContext.toLocalDate(dateTime2), new LocalDate("2015-08-31"));
+ assertEquals(internalCallContext.toLocalDate(dateTime3), new LocalDate("2015-11-30"));
+ }
+
+ @Test(groups = "fast")
+ public void testIdempotencyOfDatesManipulation() throws Exception {
+ final ImmutableList.Builder<DateTimeZone> dateTimeZoneBuilder = ImmutableList.<DateTimeZone>builder();
+ dateTimeZoneBuilder.add(DateTimeZone.forID("HST"));
+ dateTimeZoneBuilder.add(DateTimeZone.forID("PST8PDT"));
+ dateTimeZoneBuilder.add(DateTimeZone.forID("MST"));
+ dateTimeZoneBuilder.add(DateTimeZone.forID("CST6CDT"));
+ dateTimeZoneBuilder.add(DateTimeZone.forID("EST"));
+ dateTimeZoneBuilder.add(DateTimeZone.forID("Brazil/DeNoronha"));
+ dateTimeZoneBuilder.add(DateTimeZone.forID("UTC"));
+ dateTimeZoneBuilder.add(DateTimeZone.forID("CET"));
+ dateTimeZoneBuilder.add(DateTimeZone.forID("Europe/Istanbul"));
+ dateTimeZoneBuilder.add(DateTimeZone.forID("Singapore"));
+ dateTimeZoneBuilder.add(DateTimeZone.forID("Japan"));
+ dateTimeZoneBuilder.add(DateTimeZone.forID("Australia/Sydney"));
+ dateTimeZoneBuilder.add(DateTimeZone.forID("Pacific/Tongatapu"));
+ final Iterable<DateTimeZone> dateTimeZones = dateTimeZoneBuilder.build();
+
+ final ImmutableList.Builder<DateTime> referenceDateTimeBuilder = ImmutableList.<DateTime>builder();
+ referenceDateTimeBuilder.add(new DateTime(2012, 1, 1, 1, 1, 1, DateTimeZone.UTC));
+ referenceDateTimeBuilder.add(new DateTime(2012, 3, 15, 12, 42, 0, DateTimeZone.forID("PST8PDT")));
+ referenceDateTimeBuilder.add(new DateTime(2012, 11, 15, 12, 42, 0, DateTimeZone.forID("PST8PDT")));
+ final Iterable<DateTime> referenceDateTimes = referenceDateTimeBuilder.build();
+
+ DateTime currentDateTime = new DateTime(2015, 1, 1, 1, 1, DateTimeZone.UTC);
+ final DateTime endDateTime = new DateTime(2020, 1, 1, 1, 1, DateTimeZone.UTC);
+ while (currentDateTime.compareTo(endDateTime) <= 0) {
+ for (final DateTimeZone dateTimeZone : dateTimeZones) {
+ for (final DateTime referenceDateTime : referenceDateTimes) {
+ final TimeAwareContext timeAwareContext = new TimeAwareContext(dateTimeZone, referenceDateTime);
+
+ final LocalDate computedLocalDate = timeAwareContext.toLocalDate(currentDateTime);
+ final DateTime computedDateTime = timeAwareContext.toUTCDateTime(computedLocalDate);
+ final LocalDate computedLocalDate2 = timeAwareContext.toLocalDate(computedDateTime);
+
+ final String msg = String.format("currentDateTime=%s, localDate=%s, dateTime=%s, dateTimeZone=%s, referenceDateTime=%s", currentDateTime, computedLocalDate, computedDateTime, dateTimeZone, referenceDateTime);
+ Assert.assertEquals(computedLocalDate2, computedLocalDate, msg);
+ }
+ }
+
+ currentDateTime = currentDateTime.plusHours(1);
+ }
+ }
+
+ private void refreshCallContext(final DateTime effectiveDateTime, final DateTimeZone timeZone) {
+ final Account account = new MockAccountBuilder().timeZone(timeZone)
+ .createdDate(effectiveDateTime)
+ .build();
+ internalCallContext.setFixedOffsetTimeZone(AccountDateTimeUtils.getFixedOffsetTimeZone(account));
+ internalCallContext.setReferenceTime(AccountDateTimeUtils.getReferenceDateTime(account));
+ }
+}
diff --git a/util/src/test/java/org/killbill/billing/util/glue/TestUtilModule.java b/util/src/test/java/org/killbill/billing/util/glue/TestUtilModule.java
index 9dee2b4..030cd05 100644
--- a/util/src/test/java/org/killbill/billing/util/glue/TestUtilModule.java
+++ b/util/src/test/java/org/killbill/billing/util/glue/TestUtilModule.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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
@@ -18,6 +18,7 @@
package org.killbill.billing.util.glue;
+import org.killbill.billing.mock.glue.MockAccountModule;
import org.killbill.billing.mock.glue.MockTenantModule;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.subscription.api.timeline.SubscriptionBaseTimelineApi;
@@ -29,9 +30,12 @@ public class TestUtilModule extends KillBillModule {
super(configSource);
}
- // TODO STEPH this is bad-- because DefaultAuditUserApi is using SubscriptionBaseTimeline API
- public void installHack() {
+ // TODO this is bad!
+ public void installHacks() {
+ // DefaultAuditUserApi is using SubscriptionBaseTimeline API
bind(SubscriptionBaseTimelineApi.class).toInstance(Mockito.mock(SubscriptionBaseTimelineApi.class));
+ // InternalCallContextFactory is using AccountInternalApi
+ install(new MockAccountModule(configSource));
}
@Override
@@ -39,6 +43,6 @@ public class TestUtilModule extends KillBillModule {
//install(new CallContextModule());
install(new CacheModule(configSource));
install(new MockTenantModule(configSource));
- installHack();
+ installHacks();
}
}