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);
}
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();
}
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();
}
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);
}
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);
}
Aggregations