if (versionedCatalog.getCatalogName() != null && !newCatalogVersion.getCatalogName().equals(versionedCatalog.getCatalogName())) { final ValidationErrors errors = new ValidationErrors(); errors.add(String.format("Catalog name '%s' should match previous catalog name '%s'", newCatalogVersion.getCatalogName(), versionedCatalog.getCatalogName()), StandaloneCatalog.class, ""); if (c.getEffectiveDate().compareTo(newCatalogVersion.getEffectiveDate()) == 0) { final ValidationErrors errors = new ValidationErrors(); errors.add(String.format("Catalog version for effectiveDate '%s' already exists", newCatalogVersion.getEffectiveDate()), StandaloneCatalog.class, "");
final PriceList inFromPriceList; if (from.getPlanName() != null) { final Plan plan = catalog.findCurrentPlan(from.getPlanName()); inFromProduct = plan.getProduct(); inFromBillingPeriod = plan.getRecurringBillingPeriod(); inFromProductCategory = plan.getProduct().getCategory(); inFromPriceList = catalog.findCurrentPricelist(plan.getPriceListName()); } else { inFromProduct = catalog.findCurrentProduct(from.getProductName()); inFromBillingPeriod = from.getBillingPeriod(); inFromProductCategory = inFromProduct.getCategory(); inFromPriceList = from.getPriceListName() != null ? catalog.findCurrentPricelist(from.getPriceListName()) : null; final PriceList inToPriceList; if (to.getPlanName() != null) { final Plan plan = catalog.findCurrentPlan(to.getPlanName()); inToProduct = plan.getProduct(); inToBillingPeriod = plan.getRecurringBillingPeriod(); inToProductCategory = plan.getProduct().getCategory(); inToPriceList = catalog.findCurrentPricelist(plan.getPriceListName()); } else { inToProduct = catalog.findCurrentProduct(to.getProductName()); inToBillingPeriod = to.getBillingPeriod(); inToProductCategory = inToProduct.getCategory(); inToPriceList = to.getPriceListName() != null ? catalog.findCurrentPricelist(to.getPriceListName()) : null;
@Override public BillingAlignment billingAlignment(final PlanPhaseSpecifier planPhase, final DateTime requestedDate, final DateTime subscriptionStartDate) throws CatalogApiException { final StaticCatalog staticCatalog = getStaticCatalog(planPhase, requestedDate, subscriptionStartDate); return staticCatalog.billingAlignment(planPhase); }
private DefaultPriceList findPriceList(final PlanSpecifier specifier, final StaticCatalog catalog) throws CatalogApiException { DefaultPriceList result = DefaultCasePriceList.getResult(priceListCase, specifier, catalog); if (result == null) { final String priceListName = specifier.getPlanName() != null ? catalog.findCurrentPlan(specifier.getPlanName()).getPriceListName() : specifier.getPriceListName(); result = (DefaultPriceList) catalog.findCurrentPricelist(priceListName); } return result; }
final PlanPhasePriceOverride override = new DefaultPlanPhasePriceOverride("standard-monthly-evergreen", Currency.USD, null, BigDecimal.ONE, ImmutableList.<UsagePriceOverride>of()); Mockito.when(overrides.getOverrides()).thenReturn(ImmutableList.of(override)); final Plan plan = standaloneCatalogWithPriceOverride.createOrFindCurrentPlan(spec, overrides); Assert.assertTrue(plan.getName().startsWith("standard-monthly-67890-")); final Matcher m = DefaultPriceOverride.CUSTOM_PLAN_NAME_PATTERN.matcher(plan.getName()); Assert.assertNotNull(standaloneCatalogWithPriceOverride.findCurrentPlan("standard-monthly")); Assert.assertEquals(e.getCode(), ErrorCode.CAT_NO_SUCH_PLAN.getCode()); Assert.assertNotNull(standaloneCatalogWithPriceOverride.findCurrentPlan("standard-monthly-1")); Assert.assertNotNull(standaloneCatalogWithPriceOverride.findCurrentPlan("standard-monthly-12345"));
@TimedResource @GET @Path("/versions") @Produces(APPLICATION_JSON) @ApiOperation(value = "Retrieve a list of catalog versions", response = DateTime.class, responseContainer = "List") @ApiResponses(value = {}) public Response getCatalogVersions(@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 VersionedCatalog<? extends StaticCatalog> catalog = catalogUserApi.getCatalog(catalogName, null, tenantContext); final List<DateTime> result = new ArrayList<DateTime>(); for (final StaticCatalog v : catalog.getVersions()) { result.add(new DateTime(v.getEffectiveDate())); } return Response.status(Status.OK).entity(result).build(); }
@Test(groups = "fast", description = "https://github.com/killbill/killbill/issues/842") public void testCreateAmbiguousPlan() throws CatalogApiException { final DateTime now = clock.getUTCNow(); final SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("foo-monthly-12345", "Foo", ProductCategory.BASE, Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED, ImmutableList.<String>of()); final CatalogUpdater catalogUpdater = new CatalogUpdater(now, desc.getCurrency()); catalogUpdater.addSimplePlanDescriptor(desc); final StandaloneCatalog catalog = catalogUpdater.getCatalog(); assertEquals(catalog.getCurrentPlans().size(), 1); final StaticCatalog standaloneCatalogWithPriceOverride = new StandaloneCatalogWithPriceOverride(catalog, priceOverride, internalCallContext.getTenantRecordId(), internalCallContextFactory); final Plan plan = catalog.findCurrentPlan("foo-monthly-12345"); assertEquals(plan.getName(), "foo-monthly-12345"); // Verify PriceOverride logic final Plan plan2 = standaloneCatalogWithPriceOverride.findCurrentPlan("foo-monthly-12345"); assertEquals(plan2.getName(), "foo-monthly-12345"); final PlanPhase planPhase = catalog.findCurrentPhase("foo-monthly-12345-evergreen"); assertEquals(planPhase.getName(), "foo-monthly-12345-evergreen"); // Verify PriceOverride logic final PlanPhase phase2 = standaloneCatalogWithPriceOverride.findCurrentPhase("foo-monthly-12345-evergreen"); assertEquals(phase2.getName(), "foo-monthly-12345-evergreen"); }
@Override public PriceList findPriceListForPlan(final String planName, final DateTime requestedDate, final DateTime subscriptionStartDate) throws CatalogApiException { final CatalogPlanEntry entry = findCatalogPlanEntry(new PlanRequestWrapper(planName), requestedDate, subscriptionStartDate); return entry.getStaticCatalog().findCurrentPricelist(entry.getPlan().getPriceListName()); }
@Test(groups = "slow") public void testCreatePlanNoProduct() throws Exception { final StandaloneCatalog catalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarAdvanced.xml").toExternalForm(), StandaloneCatalog.class); final StaticCatalog standaloneCatalogWithPriceOverride = new StandaloneCatalogWithPriceOverride(catalog, priceOverride, internalCallContext.getTenantRecordId(), internalCallContextFactory); try { final PlanSpecifier specWithNullProduct = new PlanSpecifier(null, BillingPeriod.ANNUAL, "DEFAULT"); standaloneCatalogWithPriceOverride.createOrFindCurrentPlan(specWithNullProduct, null); Assert.fail(); } catch (final CatalogApiException e) { Assert.assertEquals(e.getCode(), ErrorCode.CAT_NULL_PRODUCT_NAME.getCode()); } }
@TimedResource @GET @Path("/availableBasePlans") @Produces(APPLICATION_JSON) @ApiOperation(value = "Retrieve available base plans", response = PlanDetailJson.class, responseContainer = "List") @ApiResponses(value = {}) public Response getAvailableBasePlans(@QueryParam(QUERY_ACCOUNT_ID) final UUID accountId, @javax.ws.rs.core.Context final HttpServletRequest request) throws CatalogApiException { final TenantContext tenantContext = accountId != null ? context.createTenantContextWithAccountId(accountId, request) : context.createTenantContextNoAccountId(request); final StaticCatalog catalog = catalogUserApi.getCurrentCatalog(catalogName, tenantContext); final List<Listing> listings = catalog.getAvailableBasePlanListings(); final List<PlanDetailJson> details = new ArrayList<PlanDetailJson>(); for (final Listing listing : listings) { details.add(new PlanDetailJson(listing)); } return Response.status(Status.OK).entity(details).build(); }
@TimedResource @GET @Path("/availableAddons") @Produces(APPLICATION_JSON) @ApiOperation(value = "Retrieve available add-ons for a given product", response = PlanDetailJson.class, responseContainer = "List") @ApiResponses(value = {}) public Response getAvailableAddons(@QueryParam("baseProductName") final String baseProductName, @Nullable @QueryParam("priceListName") final String priceListName, @QueryParam(QUERY_ACCOUNT_ID) final UUID accountId, @javax.ws.rs.core.Context final HttpServletRequest request) throws CatalogApiException { final TenantContext tenantContext = accountId != null ? context.createTenantContextWithAccountId(accountId, request) : context.createTenantContextNoAccountId(request); final StaticCatalog catalog = catalogUserApi.getCurrentCatalog(catalogName, tenantContext); final List<Listing> listings = catalog.getAvailableAddOnListings(baseProductName, priceListName); final List<PlanDetailJson> details = new ArrayList<PlanDetailJson>(); for (final Listing listing : listings) { details.add(new PlanDetailJson(listing)); } return Response.status(Status.OK).entity(details).build(); }
public DefaultBillingEvent(final SubscriptionBase subscription, final DateTime effectiveDate, final boolean isActive, final Plan plan, final PlanPhase planPhase, final BigDecimal fixedPrice, final Currency currency, final BillingPeriod billingPeriod, final int billCycleDayLocal, final String description, final long totalOrdering, final SubscriptionBaseTransitionType type, final Catalog catalog, final boolean isDisableEvent) throws CatalogApiException { this.catalog = catalog; this.subscription = subscription; this.effectiveDate = effectiveDate; this.plan = plan; this.planPhase = planPhase; this.fixedPrice = fixedPrice; this.currency = currency; this.billingPeriod = billingPeriod; this.billCycleDayLocal = billCycleDayLocal; this.description = description; this.type = type; this.totalOrdering = totalOrdering; this.usages = initializeUsage(isActive); this.isDisableEvent = isDisableEvent; this.nextPlanPhase = isDisableEvent ? null : planPhase; this.catalogEffectiveDate = plan != null ? new DateTime(plan.getCatalog().getEffectiveDate()) : null; this.billingAlignment = null; }
private DefaultPriceList findPriceList(final PlanSpecifier specifier, final StaticCatalog catalog) throws CatalogApiException { DefaultPriceList result = DefaultCasePriceList.getResult(priceListCase, specifier, catalog); if (result == null) { final String priceListName = specifier.getPlanName() != null ? catalog.findCurrentPlan(specifier.getPlanName()).getPriceListName() : specifier.getPriceListName(); result = (DefaultPriceList) catalog.findCurrentPricelist(priceListName); } return result; }
@Test(groups = "fast", description = "https://github.com/killbill/killbill/issues/842") public void testCreateAmbiguousPlan() throws CatalogApiException { final DateTime now = clock.getUTCNow(); final SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("foo-monthly-12345", "Foo", ProductCategory.BASE, Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED, ImmutableList.<String>of()); final CatalogUpdater catalogUpdater = new CatalogUpdater(now, desc.getCurrency()); catalogUpdater.addSimplePlanDescriptor(desc); final StandaloneCatalog catalog = catalogUpdater.getCatalog(); assertEquals(catalog.getCurrentPlans().size(), 1); final StaticCatalog standaloneCatalogWithPriceOverride = new StandaloneCatalogWithPriceOverride(catalog, priceOverride, internalCallContext.getTenantRecordId(), internalCallContextFactory); final Plan plan = catalog.findCurrentPlan("foo-monthly-12345"); assertEquals(plan.getName(), "foo-monthly-12345"); // Verify PriceOverride logic final Plan plan2 = standaloneCatalogWithPriceOverride.findCurrentPlan("foo-monthly-12345"); assertEquals(plan2.getName(), "foo-monthly-12345"); final PlanPhase planPhase = catalog.findCurrentPhase("foo-monthly-12345-evergreen"); assertEquals(planPhase.getName(), "foo-monthly-12345-evergreen"); // Verify PriceOverride logic final PlanPhase phase2 = standaloneCatalogWithPriceOverride.findCurrentPhase("foo-monthly-12345-evergreen"); assertEquals(phase2.getName(), "foo-monthly-12345-evergreen"); }
final PlanPhasePriceOverride override = new DefaultPlanPhasePriceOverride("standard-monthly-evergreen", Currency.USD, null, BigDecimal.ONE, ImmutableList.<UsagePriceOverride>of()); Mockito.when(overrides.getOverrides()).thenReturn(ImmutableList.of(override)); final Plan plan = standaloneCatalogWithPriceOverride.createOrFindCurrentPlan(spec, overrides); Assert.assertTrue(plan.getName().startsWith("standard-monthly-67890-")); final Matcher m = DefaultPriceOverride.CUSTOM_PLAN_NAME_PATTERN.matcher(plan.getName()); Assert.assertNotNull(standaloneCatalogWithPriceOverride.findCurrentPlan("standard-monthly")); Assert.assertEquals(e.getCode(), ErrorCode.CAT_NO_SUCH_PLAN.getCode()); Assert.assertNotNull(standaloneCatalogWithPriceOverride.findCurrentPlan("standard-monthly-1")); Assert.assertNotNull(standaloneCatalogWithPriceOverride.findCurrentPlan("standard-monthly-12345"));
public PlanChangeResult planChange(final PlanPhaseSpecifier from, final PlanSpecifier to, final StaticCatalog catalog) throws CatalogApiException { final DefaultPriceList toPriceList = to.getPriceListName() != null ? (DefaultPriceList) catalog.findCurrentPricelist(to.getPriceListName()) : findPriceList(from, catalog); // If we use old scheme {product, billingPeriod, pricelist}, ensure pricelist is correct // (Pricelist may be null because if it is unspecified this is the principal use-case) final PlanSpecifier toWithPriceList = to.getPlanName() == null ? new PlanSpecifier(to.getProductName(), to.getBillingPeriod(), toPriceList.getName()) : to; final BillingActionPolicy policy = getPlanChangePolicy(from, toWithPriceList, catalog); if (policy == BillingActionPolicy.ILLEGAL) { throw new IllegalPlanChange(from, toWithPriceList); } final PlanAlignmentChange alignment = getPlanChangeAlignment(from, toWithPriceList, catalog); return new PlanChangeResult(toPriceList, policy, alignment); }
@Test(groups = "slow") public void testCreatePlanInvalidProduct() throws Exception { final StandaloneCatalog catalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarAdvanced.xml").toExternalForm(), StandaloneCatalog.class); final StaticCatalog standaloneCatalogWithPriceOverride = new StandaloneCatalogWithPriceOverride(catalog, priceOverride, internalCallContext.getTenantRecordId(), internalCallContextFactory); try { final PlanSpecifier specWithNullProduct = new PlanSpecifier("INVALID", BillingPeriod.ANNUAL, "DEFAULT"); standaloneCatalogWithPriceOverride.createOrFindCurrentPlan(specWithNullProduct, null); Assert.fail(); } catch (final CatalogApiException e) { Assert.assertEquals(e.getCode(), ErrorCode.CAT_NO_SUCH_PRODUCT.getCode()); } } }
protected boolean satisfiesCase(final PlanSpecifier planPhase, final StaticCatalog c) throws CatalogApiException { final Product product; final BillingPeriod billingPeriod; final ProductCategory productCategory; final PriceList priceList; if (planPhase.getPlanName() != null) { final Plan plan = c.findCurrentPlan(planPhase.getPlanName()); product = plan.getProduct(); billingPeriod = plan.getRecurringBillingPeriod(); productCategory = plan.getProduct().getCategory(); priceList = c.findCurrentPricelist(plan.getPriceListName()); } else { product = c.findCurrentProduct(planPhase.getProductName()); billingPeriod = planPhase.getBillingPeriod(); productCategory = product.getCategory(); priceList = getPriceList() != null ? c.findCurrentPricelist(planPhase.getPriceListName()) : null; } return (getProduct() == null || getProduct().equals(product)) && (getProductCategory() == null || getProductCategory().equals(productCategory)) && (getBillingPeriod() == null || getBillingPeriod().equals(billingPeriod)) && (getPriceList() == null || getPriceList().equals(priceList)); }
@TimedResource @GET @Produces(APPLICATION_JSON) @ApiOperation(value = "Retrieve the catalog as JSON", responseContainer = "List", response = CatalogJson.class) @ApiResponses(value = {}) public Response getCatalogJson(@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<? extends StaticCatalog> catalog = catalogUserApi.getCatalog(catalogName, null, tenantContext); final List<CatalogJson> result = new ArrayList<CatalogJson>(); if (catalogDateVersion != null) { result.add(new CatalogJson(catalog, catalogDateVersion)); } else { for (final StaticCatalog v : catalog.getVersions()) { result.add(new CatalogJson(catalog, new DateTime(v.getEffectiveDate()))); } } return Response.status(Status.OK).entity(result).build(); }
if (versionedCatalog.getCatalogName() != null && !newCatalogVersion.getCatalogName().equals(versionedCatalog.getCatalogName())) { final ValidationErrors errors = new ValidationErrors(); errors.add(String.format("Catalog name '%s' should match previous catalog name '%s'", newCatalogVersion.getCatalogName(), versionedCatalog.getCatalogName()), StandaloneCatalog.class, ""); if (c.getEffectiveDate().compareTo(newCatalogVersion.getEffectiveDate()) == 0) { final ValidationErrors errors = new ValidationErrors(); errors.add(String.format("Catalog version for effectiveDate '%s' already exists", newCatalogVersion.getEffectiveDate()), StandaloneCatalog.class, "");