@Test(groups = "slow")
  public void testWithPriceOverride() throws Exception {

    // Create a per-tenant catalog with one plan
    final SimplePlanDescriptor desc1 =
        new DefaultSimplePlanDescriptor(
            "bar-monthly",
            "Bar",
            ProductCategory.BASE,
            account.getCurrency(),
            BigDecimal.TEN,
            BillingPeriod.MONTHLY,
            0,
            TimeUnit.UNLIMITED,
            ImmutableList.<String>of());
    catalogUserApi.addSimplePlan(desc1, init, testCallContext);
    StaticCatalog catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
    assertEquals(catalog.getCurrentPlans().size(), 1);

    final Plan plan = catalog.getCurrentPlans().iterator().next();
    final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("bar-monthly", null);

    final List<PlanPhasePriceOverride> overrides = new ArrayList<PlanPhasePriceOverride>();
    overrides.add(
        new DefaultPlanPhasePriceOverride(
            plan.getFinalPhase().getName(), account.getCurrency(), null, BigDecimal.ONE));
    final Entitlement baseEntitlement = createEntitlement(spec, overrides, true);

    List<Invoice> invoices =
        invoiceUserApi.getInvoicesByAccount(account.getId(), false, testCallContext);
    assertEquals(invoices.size(), 1);
    assertEquals(invoices.get(0).getChargedAmount().compareTo(BigDecimal.ONE), 0);

    busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
    clock.addMonths(1);
    assertListenerStatus();

    invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, testCallContext);
    assertEquals(invoices.size(), 2);
    assertEquals(invoices.get(1).getChargedAmount().compareTo(BigDecimal.ONE), 0);

    // Change plan to original (non overridden plan)
    busHandler.pushExpectedEvents(
        NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
    baseEntitlement.changePlan(spec, null, ImmutableList.<PluginProperty>of(), testCallContext);
    assertListenerStatus();

    invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, testCallContext);
    assertEquals(invoices.size(), 3);
    assertEquals(
        invoices.get(2).getChargedAmount().compareTo(new BigDecimal("9.00")),
        0); // 10 (recurring) - 1 (repair)
  }
  @Test(groups = "slow")
  public void testError_CAT_MULTIPLE_MATCHING_PLANS_FOR_PRICELIST() throws Exception {

    // Create a per-tenant catalog with one plan
    final SimplePlanDescriptor desc1 =
        new DefaultSimplePlanDescriptor(
            "zoe-monthly",
            "Zoe",
            ProductCategory.BASE,
            account.getCurrency(),
            BigDecimal.TEN,
            BillingPeriod.MONTHLY,
            0,
            TimeUnit.UNLIMITED,
            ImmutableList.<String>of());
    catalogUserApi.addSimplePlan(desc1, init, testCallContext);
    StaticCatalog catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
    assertEquals(catalog.getCurrentPlans().size(), 1);

    final SimplePlanDescriptor desc2 =
        new DefaultSimplePlanDescriptor(
            "zoe-14-monthly",
            "Zoe",
            ProductCategory.BASE,
            account.getCurrency(),
            BigDecimal.TEN,
            BillingPeriod.MONTHLY,
            14,
            TimeUnit.DAYS,
            ImmutableList.<String>of());
    catalogUserApi.addSimplePlan(desc2, init, testCallContext);
    catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
    assertEquals(catalog.getCurrentPlans().size(), 2);

    try {
      final PlanPhaseSpecifier spec =
          new PlanPhaseSpecifier(
              "Zoe", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
      entitlementApi.createBaseEntitlement(
          account.getId(),
          spec,
          UUID.randomUUID().toString(),
          null,
          null,
          null,
          false,
          ImmutableList.<PluginProperty>of(),
          testCallContext);
      fail("Creating entitlement should fail");
    } catch (final EntitlementApiException e) {
      assertEquals(e.getCode(), ErrorCode.CAT_MULTIPLE_MATCHING_PLANS_FOR_PRICELIST.getCode());
    }
  }
  private void checkForTaxCodesOnProducts(
      final Invoice invoice,
      final Collection<PluginProperty> properties,
      final TenantContext context) {
    final Map<String, String> planToProductCache = new HashMap<String, String>();
    final Map<String, String> productToTaxCodeCache = new HashMap<String, String>();

    for (final InvoiceItem invoiceItem : invoice.getInvoiceItems()) {
      final String planName = invoiceItem.getPlanName();
      if (planName == null) {
        continue;
      }

      if (planToProductCache.get(planName) == null) {
        try {
          final StaticCatalog catalog =
              killbillAPI.getCatalogUserApi().getCurrentCatalog(null, context);
          final Plan plan = catalog.findCurrentPlan(planName);
          planToProductCache.put(planName, plan.getProduct().getName());
        } catch (final CatalogApiException e) {
          continue;
        }
      }
      final String productName = planToProductCache.get(planName);
      if (productName == null) {
        continue;
      }

      if (productToTaxCodeCache.get(productName) == null) {
        try {
          final String taxCode = dao.getTaxCode(productName, context.getTenantId());
          productToTaxCodeCache.put(productName, taxCode);
        } catch (final SQLException e) {
          continue;
        }
      }

      final String taxCode = productToTaxCodeCache.get(productName);
      if (taxCode != null) {
        addTaxCodeToInvoiceItem(
            invoiceItem.getId(), productToTaxCodeCache.get(productName), properties);
      }
    }
  }
  @Test(groups = "slow")
  public void testBasic() throws Exception {

    // Create a per-tenant catalog with one plan
    final SimplePlanDescriptor desc1 =
        new DefaultSimplePlanDescriptor(
            "foo-monthly",
            "Foo",
            ProductCategory.BASE,
            account.getCurrency(),
            BigDecimal.TEN,
            BillingPeriod.MONTHLY,
            0,
            TimeUnit.UNLIMITED,
            ImmutableList.<String>of());
    catalogUserApi.addSimplePlan(desc1, init, testCallContext);
    StaticCatalog catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
    assertEquals(catalog.getCurrentPlans().size(), 1);

    final Entitlement baseEntitlement = createEntitlement("foo-monthly", true);

    invoiceChecker.checkInvoice(
        account.getId(),
        1,
        testCallContext,
        new ExpectedInvoiceItemCheck(
            new LocalDate(2016, 6, 1),
            new LocalDate(2016, 7, 1),
            InvoiceItemType.RECURRING,
            BigDecimal.TEN));

    // Add another Plan in the catalog
    final SimplePlanDescriptor desc2 =
        new DefaultSimplePlanDescriptor(
            "superfoo-monthly",
            "SuperFoo",
            ProductCategory.BASE,
            account.getCurrency(),
            new BigDecimal("20.00"),
            BillingPeriod.MONTHLY,
            0,
            TimeUnit.UNLIMITED,
            ImmutableList.<String>of());
    catalogUserApi.addSimplePlan(desc2, init, testCallContext);
    catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
    assertEquals(catalog.getCurrentPlans().size(), 2);

    // Change Plan to the newly added Plan and verify correct default rules behavior (IMMEDIATE
    // change)
    busHandler.pushExpectedEvents(
        NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
    baseEntitlement.changePlan(
        new PlanSpecifier("SuperFoo", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME),
        null,
        ImmutableList.<PluginProperty>of(),
        testCallContext);
    assertListenerStatus();

    invoiceChecker.checkInvoice(
        account.getId(),
        2,
        testCallContext,
        new ExpectedInvoiceItemCheck(
            new LocalDate(2016, 6, 1),
            new LocalDate(2016, 7, 1),
            InvoiceItemType.RECURRING,
            new BigDecimal("20.00")),
        new ExpectedInvoiceItemCheck(
            new LocalDate(2016, 6, 1),
            new LocalDate(2016, 7, 1),
            InvoiceItemType.REPAIR_ADJ,
            new BigDecimal("-10.00")));
    assertListenerStatus();
  }
  @Test(groups = "slow")
  public void testWith$0RecurringPlan() throws Exception {

    // Create a per-tenant catalog with one plan
    final SimplePlanDescriptor zeroDesc =
        new DefaultSimplePlanDescriptor(
            "zeroDesc-monthly",
            "Zero",
            ProductCategory.BASE,
            account.getCurrency(),
            BigDecimal.ZERO,
            BillingPeriod.MONTHLY,
            0,
            TimeUnit.UNLIMITED,
            ImmutableList.<String>of());
    catalogUserApi.addSimplePlan(zeroDesc, init, testCallContext);
    StaticCatalog catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
    assertEquals(catalog.getCurrentPlans().size(), 1);

    final PlanPhaseSpecifier specZero = new PlanPhaseSpecifier("zeroDesc-monthly", null);

    busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
    final Entitlement baseEntitlement =
        entitlementApi.createBaseEntitlement(
            account.getId(),
            specZero,
            UUID.randomUUID().toString(),
            ImmutableList.<PlanPhasePriceOverride>of(),
            null,
            null,
            false,
            ImmutableList.<PluginProperty>of(),
            testCallContext);
    assertListenerStatus();

    Subscription refreshedBaseEntitlement =
        subscriptionApi.getSubscriptionForEntitlementId(baseEntitlement.getId(), testCallContext);
    assertEquals(refreshedBaseEntitlement.getChargedThroughDate(), new LocalDate(2016, 7, 1));

    busHandler.pushExpectedEvents(NextEvent.INVOICE);
    clock.addMonths(1);
    assertListenerStatus();

    refreshedBaseEntitlement =
        subscriptionApi.getSubscriptionForEntitlementId(baseEntitlement.getId(), testCallContext);
    assertEquals(refreshedBaseEntitlement.getChargedThroughDate(), new LocalDate(2016, 8, 1));

    busHandler.pushExpectedEvents(NextEvent.INVOICE);
    clock.addMonths(1);
    assertListenerStatus();

    refreshedBaseEntitlement =
        subscriptionApi.getSubscriptionForEntitlementId(baseEntitlement.getId(), testCallContext);
    assertEquals(refreshedBaseEntitlement.getChargedThroughDate(), new LocalDate(2016, 9, 1));

    // Add another Plan in the catalog
    final SimplePlanDescriptor descNonZero =
        new DefaultSimplePlanDescriptor(
            "superfoo-monthly",
            "SuperFoo",
            ProductCategory.BASE,
            account.getCurrency(),
            new BigDecimal("20.00"),
            BillingPeriod.MONTHLY,
            0,
            TimeUnit.UNLIMITED,
            ImmutableList.<String>of());
    catalogUserApi.addSimplePlan(descNonZero, init, testCallContext);
    catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
    assertEquals(catalog.getCurrentPlans().size(), 2);

    final PlanPhaseSpecifier specNonZero = new PlanPhaseSpecifier("superfoo-monthly", null);

    busHandler.pushExpectedEvents(
        NextEvent.CREATE,
        NextEvent.BLOCK,
        NextEvent.INVOICE,
        NextEvent.INVOICE_PAYMENT,
        NextEvent.PAYMENT);
    final Entitlement baseEntitlement2 =
        entitlementApi.createBaseEntitlement(
            account.getId(),
            specNonZero,
            UUID.randomUUID().toString(),
            ImmutableList.<PlanPhasePriceOverride>of(),
            null,
            null,
            false,
            ImmutableList.<PluginProperty>of(),
            testCallContext);
    assertListenerStatus();

    busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
    clock.addMonths(1);
    assertListenerStatus();

    refreshedBaseEntitlement =
        subscriptionApi.getSubscriptionForEntitlementId(baseEntitlement.getId(), testCallContext);
    assertEquals(refreshedBaseEntitlement.getChargedThroughDate(), new LocalDate(2016, 10, 1));

    Subscription refreshedBaseEntitlement2 =
        subscriptionApi.getSubscriptionForEntitlementId(baseEntitlement2.getId(), testCallContext);
    assertEquals(refreshedBaseEntitlement2.getChargedThroughDate(), new LocalDate(2016, 10, 1));

    busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
    clock.addMonths(1);
    assertListenerStatus();

    refreshedBaseEntitlement =
        subscriptionApi.getSubscriptionForEntitlementId(baseEntitlement.getId(), testCallContext);
    assertEquals(refreshedBaseEntitlement.getChargedThroughDate(), new LocalDate(2016, 11, 1));

    refreshedBaseEntitlement2 =
        subscriptionApi.getSubscriptionForEntitlementId(baseEntitlement2.getId(), testCallContext);
    assertEquals(refreshedBaseEntitlement2.getChargedThroughDate(), new LocalDate(2016, 11, 1));

    busHandler.pushExpectedEvents(NextEvent.BLOCK);
    baseEntitlement.cancelEntitlementWithDateOverrideBillingPolicy(
        clock.getUTCToday(),
        BillingActionPolicy.END_OF_TERM,
        ImmutableList.<PluginProperty>of(),
        testCallContext);
    assertListenerStatus();

    busHandler.pushExpectedEvents(
        NextEvent.CANCEL,
        NextEvent.NULL_INVOICE,
        NextEvent.INVOICE,
        NextEvent.INVOICE_PAYMENT,
        NextEvent.PAYMENT);
    clock.addMonths(1);
    assertListenerStatus();

    refreshedBaseEntitlement =
        subscriptionApi.getSubscriptionForEntitlementId(baseEntitlement.getId(), testCallContext);
    assertEquals(refreshedBaseEntitlement.getChargedThroughDate(), new LocalDate(2016, 11, 1));

    refreshedBaseEntitlement2 =
        subscriptionApi.getSubscriptionForEntitlementId(baseEntitlement2.getId(), testCallContext);
    assertEquals(refreshedBaseEntitlement2.getChargedThroughDate(), new LocalDate(2016, 12, 1));

    busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
    clock.addMonths(1);
    assertListenerStatus();

    refreshedBaseEntitlement =
        subscriptionApi.getSubscriptionForEntitlementId(baseEntitlement.getId(), testCallContext);
    assertEquals(refreshedBaseEntitlement.getChargedThroughDate(), new LocalDate(2016, 11, 1));

    refreshedBaseEntitlement2 =
        subscriptionApi.getSubscriptionForEntitlementId(baseEntitlement2.getId(), testCallContext);
    assertEquals(refreshedBaseEntitlement2.getChargedThroughDate(), new LocalDate(2017, 1, 1));
  }
  // Use custom plan definition to create a THIRTY_DAYS plan with no trial and test issue #598
  @Test(groups = "slow")
  public void testWithThirtyDaysPlan() throws Exception {

    // Create a per-tenant catalog with one plan
    final SimplePlanDescriptor desc1 =
        new DefaultSimplePlanDescriptor(
            "thirty-monthly",
            "Thirty",
            ProductCategory.BASE,
            account.getCurrency(),
            BigDecimal.TEN,
            BillingPeriod.THIRTY_DAYS,
            0,
            TimeUnit.UNLIMITED,
            ImmutableList.<String>of());
    catalogUserApi.addSimplePlan(desc1, init, testCallContext);
    StaticCatalog catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
    assertEquals(catalog.getCurrentPlans().size(), 1);

    final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("thirty-monthly", null);

    createEntitlement(spec, null, true);

    List<Invoice> invoices =
        invoiceUserApi.getInvoicesByAccount(account.getId(), false, testCallContext);
    assertEquals(invoices.size(), 1);
    assertEquals(invoices.get(0).getChargedAmount().compareTo(BigDecimal.TEN), 0);
    assertEquals(invoices.get(0).getInvoiceItems().size(), 1);

    final List<ExpectedInvoiceItemCheck> expectedInvoices =
        new ArrayList<ExpectedInvoiceItemCheck>();
    expectedInvoices.add(
        new ExpectedInvoiceItemCheck(
            new LocalDate(2016, 6, 1),
            new LocalDate(2016, 7, 1),
            InvoiceItemType.RECURRING,
            BigDecimal.TEN));
    invoiceChecker.checkInvoiceNoAudits(invoices.get(0), callContext, expectedInvoices);

    int invoiceSize = 2;
    LocalDate startDate = new LocalDate(2016, 7, 1);
    for (int i = 0; i < 14; i++) {

      expectedInvoices.clear();

      busHandler.pushExpectedEvents(
          NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
      clock.addDays(30);
      assertListenerStatus();

      LocalDate endDate = startDate.plusDays(30);
      invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, testCallContext);
      assertEquals(invoices.size(), invoiceSize);

      expectedInvoices.add(
          new ExpectedInvoiceItemCheck(
              startDate, endDate, InvoiceItemType.RECURRING, BigDecimal.TEN));
      invoiceChecker.checkInvoiceNoAudits(
          invoices.get(invoices.size() - 1), callContext, expectedInvoices);

      startDate = endDate;
      invoiceSize++;
    }
  }
  @Test(groups = "slow")
  public void testWithMultiplePlansForOneProduct()
      throws CatalogApiException, EntitlementApiException {

    // Create a per-tenant catalog with one plan
    final SimplePlanDescriptor desc1 =
        new DefaultSimplePlanDescriptor(
            "xxx-monthly",
            "XXX",
            ProductCategory.BASE,
            account.getCurrency(),
            BigDecimal.TEN,
            BillingPeriod.MONTHLY,
            0,
            TimeUnit.UNLIMITED,
            ImmutableList.<String>of());
    catalogUserApi.addSimplePlan(desc1, init, testCallContext);
    StaticCatalog catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
    assertEquals(catalog.getCurrentProducts().size(), 1);
    assertEquals(catalog.getCurrentPlans().size(), 1);

    final Entitlement baseEntitlement1 = createEntitlement("xxx-monthly", true);

    // Add a second plan for same product but with a 14 days trial
    final SimplePlanDescriptor desc2 =
        new DefaultSimplePlanDescriptor(
            "xxx-14-monthly",
            "XXX",
            ProductCategory.BASE,
            account.getCurrency(),
            BigDecimal.TEN,
            BillingPeriod.MONTHLY,
            14,
            TimeUnit.DAYS,
            ImmutableList.<String>of());
    catalogUserApi.addSimplePlan(desc2, init, testCallContext);
    catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
    assertEquals(catalog.getCurrentProducts().size(), 1);
    assertEquals(catalog.getCurrentPlans().size(), 2);

    final Entitlement baseEntitlement2 = createEntitlement("xxx-14-monthly", false);

    // Add a second plan for same product but with a 30 days trial
    final SimplePlanDescriptor desc3 =
        new DefaultSimplePlanDescriptor(
            "xxx-30-monthly",
            "XXX",
            ProductCategory.BASE,
            account.getCurrency(),
            BigDecimal.TEN,
            BillingPeriod.MONTHLY,
            30,
            TimeUnit.DAYS,
            ImmutableList.<String>of());
    catalogUserApi.addSimplePlan(desc3, init, testCallContext);
    catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
    assertEquals(catalog.getCurrentProducts().size(), 1);
    assertEquals(catalog.getCurrentPlans().size(), 3);

    final Entitlement baseEntitlement3 = createEntitlement("xxx-30-monthly", false);

    // Move clock 14 days
    busHandler.pushExpectedEvents(
        NextEvent.PHASE, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
    clock.addDays(14);
    assertListenerStatus();

    // Move clock 16 days
    busHandler.pushExpectedEvents(
        NextEvent.PHASE,
        NextEvent.INVOICE,
        NextEvent.NULL_INVOICE,
        NextEvent.INVOICE_PAYMENT,
        NextEvent.PAYMENT);
    clock.addDays(16);
    assertListenerStatus();
  }