Search in sources :

Example 26 with VersionedCatalog

use of org.killbill.billing.catalog.api.VersionedCatalog in project killbill by killbill.

the class TestCatalogRetireElements method testChangePlanTwiceWithNewPlan.

@Test(groups = "slow", description = "See https://github.com/killbill/killbill/issues/1110")
public void testChangePlanTwiceWithNewPlan() throws Exception {
    // Catalog v1 starts in 2011-01-01
    // Catalog v2 starts in 2015-12-01
    // -> Start on catalog V1
    final LocalDate today = new LocalDate(2015, 11, 5);
    // 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(null));
    final String productName = "Shotgun";
    final BillingPeriod term = BillingPeriod.MONTHLY;
    final PlanPhaseSpecifier spec1 = new PlanPhaseSpecifier(productName, term, "DEFAULT", null);
    busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
    final UUID bpEntitlementId = entitlementApi.createBaseEntitlement(account.getId(), new DefaultEntitlementSpecifier(spec1), "externalKey", null, null, false, true, ImmutableList.<PluginProperty>of(), callContext);
    assertListenerStatus();
    Entitlement bpEntitlement = entitlementApi.getEntitlementForId(bpEntitlementId, callContext);
    // Move out a month. Date > catalog V2
    busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
    clock.addMonths(1);
    assertListenerStatus();
    // Current date is > catalog V2
    // Change to a plan that exists in V2 but not in V1
    final PlanPhaseSpecifier spec2 = new PlanPhaseSpecifier("bazooka-monthly", null);
    busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
    bpEntitlement = bpEntitlement.changePlanWithDate(new DefaultEntitlementSpecifier(spec2), clock.getUTCToday(), ImmutableList.<PluginProperty>of(), callContext);
    assertListenerStatus();
    // Change back to original plan: The code (subscription) chooses the latest version of the catalog when making the change and therefore the call succeeds
    busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE);
    bpEntitlement.changePlanWithDate(new DefaultEntitlementSpecifier(spec1), clock.getUTCToday(), ImmutableList.<PluginProperty>of(), callContext);
    assertListenerStatus();
    // 
    // The code normally goes through the grandfathering logic to find the version but specifies the transitionTime of the latest CHANGE (and not the subscriptionStartDate)
    // and therefore correctly find the latest catalog version, invoicing at the new price 295.95
    // 
    Invoice curInvoice = invoiceChecker.checkInvoice(account.getId(), 4, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2015, 12, 5), new LocalDate(2016, 1, 5), InvoiceItemType.RECURRING, new BigDecimal("295.95")), new ExpectedInvoiceItemCheck(new LocalDate(2015, 12, 5), new LocalDate(2016, 1, 5), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-500.00")), new ExpectedInvoiceItemCheck(new LocalDate(2015, 12, 5), new LocalDate(2015, 12, 5), InvoiceItemType.CBA_ADJ, new BigDecimal("204.05")));
    final VersionedCatalog catalog = catalogUserApi.getCatalog("foo", callContext);
    // RECURRING should be set against V2
    Assert.assertEquals(curInvoice.getInvoiceItems().get(0).getCatalogEffectiveDate().toDate().compareTo(catalog.getVersions().get(1).getEffectiveDate()), 0);
    Assert.assertNull(curInvoice.getInvoiceItems().get(1).getCatalogEffectiveDate());
    Assert.assertNull(curInvoice.getInvoiceItems().get(2).getCatalogEffectiveDate());
    final Subscription bpSubscription = subscriptionApi.getSubscriptionForEntitlementId(bpEntitlementId, callContext);
    final List<SubscriptionEvent> events = bpSubscription.getSubscriptionEvents();
    // We are seeing START_ENTITLEMENT, START_BILLING, and the **last CHANGE**
    // Note that the PHASE and intermediate CHANGE are not being returned (is_active = '0') because all coincided on the same date. This is debatable
    // whether this is a good semantics. See #1030
    assertEquals(events.size(), 3);
    // Verify what we return is the price from the correct catalog version. See #1120
    assertEquals(events.get(2).getNextPhase().getRecurring().getRecurringPrice().getPrice(account.getCurrency()).compareTo(new BigDecimal("295.95")), 0);
}
Also used : PlanPhaseSpecifier(org.killbill.billing.catalog.api.PlanPhaseSpecifier) SubscriptionEvent(org.killbill.billing.entitlement.api.SubscriptionEvent) Account(org.killbill.billing.account.api.Account) Invoice(org.killbill.billing.invoice.api.Invoice) BillingPeriod(org.killbill.billing.catalog.api.BillingPeriod) LocalDate(org.joda.time.LocalDate) ExpectedInvoiceItemCheck(org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck) BigDecimal(java.math.BigDecimal) DefaultEntitlementSpecifier(org.killbill.billing.entitlement.api.DefaultEntitlementSpecifier) PluginProperty(org.killbill.billing.payment.api.PluginProperty) VersionedCatalog(org.killbill.billing.catalog.api.VersionedCatalog) UUID(java.util.UUID) DefaultEntitlement(org.killbill.billing.entitlement.api.DefaultEntitlement) Entitlement(org.killbill.billing.entitlement.api.Entitlement) Subscription(org.killbill.billing.entitlement.api.Subscription) Test(org.testng.annotations.Test)

Example 27 with VersionedCatalog

use of org.killbill.billing.catalog.api.VersionedCatalog in project killbill by killbill.

the class AdminResource method invalidatesCacheByTenant.

@DELETE
@Path("/" + CACHE + "/" + TENANTS)
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Invalidates Caches per tenant level")
@ApiResponses(value = { @ApiResponse(code = 204, message = "Successful operation") })
public Response invalidatesCacheByTenant(@javax.ws.rs.core.Context final HttpServletRequest request) throws TenantApiException {
    // creating Tenant Context from Request
    final TenantContext tenantContext = context.createTenantContextNoAccountId(request);
    final Tenant currentTenant = tenantApi.getTenantById(tenantContext.getTenantId());
    // getting Tenant Record Id
    final Long tenantRecordId = recordIdApi.getRecordId(tenantContext.getTenantId(), ObjectType.TENANT, tenantContext);
    final Function<String, Boolean> tenantKeysMatcher = new Function<String, Boolean>() {

        @Override
        public Boolean apply(@Nullable final String key) {
            return key != null && key.endsWith("::" + tenantRecordId);
        }
    };
    // clear tenant-record-id cache by tenantId
    final CacheController<String, Long> tenantRecordIdCacheController = cacheControllerDispatcher.getCacheController(CacheType.TENANT_RECORD_ID);
    tenantRecordIdCacheController.remove(currentTenant.getId().toString());
    // clear tenant-payment-state-machine-config cache by tenantRecordId
    final CacheController<String, Object> tenantPaymentStateMachineConfigCacheController = cacheControllerDispatcher.getCacheController(CacheType.TENANT_PAYMENT_STATE_MACHINE_CONFIG);
    tenantPaymentStateMachineConfigCacheController.remove(tenantKeysMatcher);
    // clear tenant cache by tenantApiKey
    final CacheController<String, Tenant> tenantCacheController = cacheControllerDispatcher.getCacheController(CacheType.TENANT);
    tenantCacheController.remove(currentTenant.getApiKey());
    // clear tenant-kv cache by tenantRecordId
    final CacheController<String, String> tenantKVCacheController = cacheControllerDispatcher.getCacheController(CacheType.TENANT_KV);
    tenantKVCacheController.remove(tenantKeysMatcher);
    // clear tenant-config cache by tenantRecordId
    final CacheController<Long, PerTenantConfig> tenantConfigCacheController = cacheControllerDispatcher.getCacheController(CacheType.TENANT_CONFIG);
    tenantConfigCacheController.remove(tenantRecordId);
    // clear tenant-overdue-config cache by tenantRecordId
    final CacheController<Long, Object> tenantOverdueConfigCacheController = cacheControllerDispatcher.getCacheController(CacheType.TENANT_OVERDUE_CONFIG);
    tenantOverdueConfigCacheController.remove(tenantRecordId);
    // clear tenant-catalog cache by tenantRecordId
    final CacheController<Long, VersionedCatalog> tenantCatalogCacheController = cacheControllerDispatcher.getCacheController(CacheType.TENANT_CATALOG);
    tenantCatalogCacheController.remove(tenantRecordId);
    return Response.status(Status.NO_CONTENT).build();
}
Also used : TenantContext(org.killbill.billing.util.callcontext.TenantContext) PerTenantConfig(org.killbill.billing.util.config.tenant.PerTenantConfig) Function(com.google.common.base.Function) VersionedCatalog(org.killbill.billing.catalog.api.VersionedCatalog) Tenant(org.killbill.billing.tenant.api.Tenant) Nullable(javax.annotation.Nullable) Path(javax.ws.rs.Path) DELETE(javax.ws.rs.DELETE) Produces(javax.ws.rs.Produces) ApiOperation(io.swagger.annotations.ApiOperation) ApiResponses(io.swagger.annotations.ApiResponses)

Example 28 with VersionedCatalog

use of org.killbill.billing.catalog.api.VersionedCatalog in project killbill by killbill.

the class CatalogResource method getCatalogXmlOriginal.

// 
// We mark this resource as hidden from a swagger point of view and create another one with a different Path below
// to hack around the restrictions of having only one type of HTTP verb per Path
// see https://github.com/killbill/killbill/issues/913
// 
@TimedResource
@GET
@Produces(TEXT_XML)
@ApiOperation(value = "Retrieve the full catalog as XML", response = String.class, hidden = true)
@ApiResponses(value = {})
public Response getCatalogXmlOriginal(@QueryParam(QUERY_REQUESTED_DT) final String requestedDate, @QueryParam(QUERY_ACCOUNT_ID) final UUID accountId, @javax.ws.rs.core.Context final HttpServletRequest request) throws Exception {
    final TenantContext tenantContext = accountId != null ? context.createTenantContextWithAccountId(accountId, request) : context.createTenantContextNoAccountId(request);
    final DateTime catalogDateVersion = requestedDate != null ? DATE_TIME_FORMATTER.parseDateTime(requestedDate).toDateTime(DateTimeZone.UTC) : null;
    final VersionedCatalog versionedcatalog = catalogUserApi.getCatalog(catalogName, tenantContext);
    final VersionedCatalog catalog;
    if (catalogDateVersion == null) {
        catalog = versionedcatalog;
    } else {
        // We have no other choice than to deep copy the catalog...
        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
        final ObjectOutput out = new ObjectOutputStream(bos);
        out.writeObject(versionedcatalog);
        final ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        final ObjectInputStream in = new ObjectInputStream(bis);
        catalog = (VersionedCatalog) in.readObject();
        catalog.getVersions().clear();
        catalog.getVersions().add(versionedcatalog.getVersion(catalogDateVersion.toDate()));
    }
    // This assumes serializableClass has the right JAXB annotations
    final Class serializableClass = catalog.getClass();
    final String result = XMLWriter.writeXML(catalog, serializableClass);
    return Response.status(Status.OK).entity(result).build();
}
Also used : VersionedCatalog(org.killbill.billing.catalog.api.VersionedCatalog) ObjectOutput(java.io.ObjectOutput) ByteArrayInputStream(java.io.ByteArrayInputStream) TenantContext(org.killbill.billing.util.callcontext.TenantContext) ByteArrayOutputStream(java.io.ByteArrayOutputStream) ObjectOutputStream(java.io.ObjectOutputStream) DateTime(org.joda.time.DateTime) ObjectInputStream(java.io.ObjectInputStream) TimedResource(org.killbill.commons.metrics.TimedResource) Produces(javax.ws.rs.Produces) GET(javax.ws.rs.GET) ApiOperation(io.swagger.annotations.ApiOperation) ApiResponses(io.swagger.annotations.ApiResponses)

Example 29 with VersionedCatalog

use of org.killbill.billing.catalog.api.VersionedCatalog in project killbill by killbill.

the class TestInArrearWithCatalogVersions method testWithChangeWithinCatalog.

@Test(groups = "slow")
public void testWithChangeWithinCatalog() throws Exception {
    // 30 days month
    clock.setDay(new LocalDate(2016, 4, 1));
    final AccountData accountData = getAccountData(1);
    final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
    accountChecker.checkAccount(account.getId(), accountData, callContext);
    final PlanPhaseSpecifier spec1 = new PlanPhaseSpecifier("electricity-monthly");
    busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
    final UUID entitlementId = entitlementApi.createBaseEntitlement(account.getId(), new DefaultEntitlementSpecifier(spec1), null, null, null, false, true, ImmutableList.<PluginProperty>of(), callContext);
    assertListenerStatus();
    recordUsageData(entitlementId, "t1", "kilowatt-hour", new LocalDate(2016, 4, 1), 143L, callContext);
    recordUsageData(entitlementId, "t2", "kilowatt-hour", new LocalDate(2016, 4, 18), 57L, callContext);
    busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
    clock.addMonths(1);
    assertListenerStatus();
    Invoice curInvoice = invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2016, 4, 1), new LocalDate(2016, 5, 1), InvoiceItemType.USAGE, new BigDecimal("300.00")));
    invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("t1", "t2"), internalCallContext);
    // -> Uses v1 : $150
    recordUsageData(entitlementId, "t3", "kilowatt-hour", new LocalDate(2016, 5, 2), 100L, callContext);
    final Entitlement bp = entitlementApi.getEntitlementForId(entitlementId, callContext);
    final PlanPhaseSpecifier spec2 = new PlanPhaseSpecifier("electricity-monthly-special");
    bp.changePlanWithDate(new DefaultEntitlementSpecifier(spec2), new LocalDate("2016-05-07"), null, callContext);
    assertListenerStatus();
    busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
    clock.addDays(8);
    assertListenerStatus();
    curInvoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 1), new LocalDate(2016, 5, 7), InvoiceItemType.USAGE, new BigDecimal("150.00")));
    invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("t3"), internalCallContext);
    // -> Uses special plan : $100
    recordUsageData(entitlementId, "t4", "kilowatt-hour", new LocalDate(2016, 5, 10), 100L, callContext);
    busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
    clock.addDays(23);
    assertListenerStatus();
    curInvoice = invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 7), new LocalDate(2016, 6, 1), InvoiceItemType.USAGE, new BigDecimal("100.00")));
    invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("t4"), internalCallContext);
    // Check item catalogEffectiveDate correctly reflects the first catalog where such plan is available
    final VersionedCatalog catalog = catalogUserApi.getCatalog("foo", callContext);
    assertEquals(curInvoice.getInvoiceItems().get(0).getCatalogEffectiveDate().toDate().compareTo(catalog.getVersions().get(0).getEffectiveDate()), 0);
}
Also used : PlanPhaseSpecifier(org.killbill.billing.catalog.api.PlanPhaseSpecifier) DefaultEntitlementSpecifier(org.killbill.billing.entitlement.api.DefaultEntitlementSpecifier) Account(org.killbill.billing.account.api.Account) VersionedCatalog(org.killbill.billing.catalog.api.VersionedCatalog) Invoice(org.killbill.billing.invoice.api.Invoice) AccountData(org.killbill.billing.account.api.AccountData) UUID(java.util.UUID) Entitlement(org.killbill.billing.entitlement.api.Entitlement) LocalDate(org.joda.time.LocalDate) ExpectedInvoiceItemCheck(org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck) BigDecimal(java.math.BigDecimal) Test(org.testng.annotations.Test)

Example 30 with VersionedCatalog

use of org.killbill.billing.catalog.api.VersionedCatalog in project killbill by killbill.

the class TestInArrearWithCatalogVersions method testWithChangeAcrossCatalogs.

@Test(groups = "slow")
public void testWithChangeAcrossCatalogs() throws Exception {
    // 30 days month
    clock.setDay(new LocalDate(2016, 4, 1));
    final AccountData accountData = getAccountData(1);
    final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
    accountChecker.checkAccount(account.getId(), accountData, callContext);
    final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("electricity-monthly");
    busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
    final UUID entitlementId = entitlementApi.createBaseEntitlement(account.getId(), new DefaultEntitlementSpecifier(spec), null, null, null, false, true, ImmutableList.<PluginProperty>of(), callContext);
    assertListenerStatus();
    recordUsageData(entitlementId, "t1", "kilowatt-hour", new LocalDate(2016, 4, 1), 143L, callContext);
    recordUsageData(entitlementId, "t2", "kilowatt-hour", new LocalDate(2016, 4, 18), 57L, callContext);
    busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
    clock.addMonths(1);
    assertListenerStatus();
    Invoice curInvoice = invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2016, 4, 1), new LocalDate(2016, 5, 1), InvoiceItemType.USAGE, new BigDecimal("300.00")));
    invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("t1", "t2"), internalCallContext);
    // -> Uses v1 : $150
    recordUsageData(entitlementId, "t3", "kilowatt-hour", new LocalDate(2016, 5, 2), 100L, callContext);
    // Catalog change with new price on 2016-05-08
    // -> Uses v2 : $250
    recordUsageData(entitlementId, "t4", "kilowatt-hour", new LocalDate(2016, 5, 10), 100L, callContext);
    busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
    clock.addMonths(1);
    assertListenerStatus();
    curInvoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 1), new LocalDate(2016, 5, 8), InvoiceItemType.USAGE, new BigDecimal("150.00")), new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 8), new LocalDate(2016, 6, 1), InvoiceItemType.USAGE, new BigDecimal("250.00")));
    invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("t3", "t4"), internalCallContext);
    // Check items catalogEffectiveDate are correctly marked against each version
    final VersionedCatalog catalog = catalogUserApi.getCatalog("foo", callContext);
    assertEquals(curInvoice.getInvoiceItems().get(0).getCatalogEffectiveDate().toDate().compareTo(catalog.getVersions().get(0).getEffectiveDate()), 0);
    assertEquals(curInvoice.getInvoiceItems().get(1).getCatalogEffectiveDate().toDate().compareTo(catalog.getVersions().get(1).getEffectiveDate()), 0);
}
Also used : PlanPhaseSpecifier(org.killbill.billing.catalog.api.PlanPhaseSpecifier) DefaultEntitlementSpecifier(org.killbill.billing.entitlement.api.DefaultEntitlementSpecifier) Account(org.killbill.billing.account.api.Account) VersionedCatalog(org.killbill.billing.catalog.api.VersionedCatalog) Invoice(org.killbill.billing.invoice.api.Invoice) AccountData(org.killbill.billing.account.api.AccountData) UUID(java.util.UUID) LocalDate(org.joda.time.LocalDate) ExpectedInvoiceItemCheck(org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck) BigDecimal(java.math.BigDecimal) Test(org.testng.annotations.Test)

Aggregations

VersionedCatalog (org.killbill.billing.catalog.api.VersionedCatalog)33 Test (org.testng.annotations.Test)14 DefaultVersionedCatalog (org.killbill.billing.catalog.DefaultVersionedCatalog)11 UUID (java.util.UUID)10 DateTime (org.joda.time.DateTime)8 LocalDate (org.joda.time.LocalDate)8 BigDecimal (java.math.BigDecimal)7 ArrayList (java.util.ArrayList)7 Account (org.killbill.billing.account.api.Account)7 ExpectedInvoiceItemCheck (org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck)7 PlanPhaseSpecifier (org.killbill.billing.catalog.api.PlanPhaseSpecifier)7 Invoice (org.killbill.billing.invoice.api.Invoice)7 ImmutableList (com.google.common.collect.ImmutableList)6 List (java.util.List)6 InternalTenantContext (org.killbill.billing.callcontext.InternalTenantContext)6 StaticCatalog (org.killbill.billing.catalog.api.StaticCatalog)6 DefaultEntitlementSpecifier (org.killbill.billing.entitlement.api.DefaultEntitlementSpecifier)6 CatalogApiException (org.killbill.billing.catalog.api.CatalogApiException)5 ApiOperation (io.swagger.annotations.ApiOperation)4 ApiResponses (io.swagger.annotations.ApiResponses)4