use of org.killbill.billing.callcontext.InternalTenantContext in project killbill by killbill.
the class TestEhCacheOverdueConfigCache method testExistingTenantOverdue.
//
// Verify OverdueConfigCache returns per tenant overdue config:
// 1. We first mock TenantInternalApi to return a different overdue config than the default one
// 2. We then mock TenantInternalApi to throw RuntimeException which means overdue config was cached and there was no additional call
// to the TenantInternalApi api (otherwise test would fail with RuntimeException)
//
@Test(groups = "fast")
public void testExistingTenantOverdue() throws OverdueApiException, URISyntaxException, IOException {
final InternalCallContext differentMultiTenantContext = Mockito.mock(InternalCallContext.class);
Mockito.when(differentMultiTenantContext.getTenantRecordId()).thenReturn(55667788L);
final AtomicBoolean shouldThrow = new AtomicBoolean(false);
final Long multiTenantRecordId = multiTenantContext.getTenantRecordId();
final Long otherMultiTenantRecordId = otherMultiTenantContext.getTenantRecordId();
final InputStream tenantInputOverdueConfig = UriAccessor.accessUri(new URI(Resources.getResource("OverdueConfig2.xml").toExternalForm()));
final String tenantOverdueConfigXML = CharStreams.toString(new InputStreamReader(tenantInputOverdueConfig, "UTF-8"));
final InputStream otherTenantInputOverdueConfig = UriAccessor.accessUri(new URI(Resources.getResource("OverdueConfig.xml").toExternalForm()));
final String otherTenantOverdueConfigXML = CharStreams.toString(new InputStreamReader(otherTenantInputOverdueConfig, "UTF-8"));
Mockito.when(tenantInternalApi.getTenantOverdueConfig(Mockito.any(InternalTenantContext.class))).thenAnswer(new Answer<String>() {
@Override
public String answer(final InvocationOnMock invocation) throws Throwable {
if (shouldThrow.get()) {
throw new RuntimeException();
}
final InternalTenantContext internalContext = (InternalTenantContext) invocation.getArguments()[0];
if (multiTenantRecordId.equals(internalContext.getTenantRecordId())) {
return tenantOverdueConfigXML;
} else if (otherMultiTenantRecordId.equals(internalContext.getTenantRecordId())) {
return otherTenantOverdueConfigXML;
} else {
return null;
}
}
});
// Verify the lookup for a non-cached tenant. No system config is set yet but EhCacheOverdueConfigCache returns a default no-op one
OverdueConfig differentResult = overdueConfigCache.getOverdueConfig(differentMultiTenantContext);
Assert.assertNotNull(differentResult);
Assert.assertEquals(differentResult.getOverdueStatesAccount().getStates().length, 1);
Assert.assertTrue(differentResult.getOverdueStatesAccount().getStates()[0].isClearState());
// Make sure the cache loader isn't invoked, see https://github.com/killbill/killbill/issues/298
shouldThrow.set(true);
differentResult = overdueConfigCache.getOverdueConfig(differentMultiTenantContext);
Assert.assertNotNull(differentResult);
Assert.assertEquals(differentResult.getOverdueStatesAccount().getStates().length, 1);
Assert.assertTrue(differentResult.getOverdueStatesAccount().getStates()[0].isClearState());
shouldThrow.set(false);
// Set a default config
overdueConfigCache.loadDefaultOverdueConfig(Resources.getResource("OverdueConfig.xml").toExternalForm());
// Verify the lookup for this tenant
final OverdueConfig result = overdueConfigCache.getOverdueConfig(multiTenantContext);
Assert.assertNotNull(result);
Assert.assertEquals(result.getOverdueStatesAccount().getStates().length, 1);
Assert.assertFalse(result.getOverdueStatesAccount().getStates()[0].isClearState());
// Verify the lookup for another tenant
final OverdueConfig otherResult = overdueConfigCache.getOverdueConfig(otherMultiTenantContext);
Assert.assertNotNull(otherResult);
Assert.assertEquals(otherResult.getOverdueStatesAccount().getStates().length, 1);
Assert.assertTrue(otherResult.getOverdueStatesAccount().getStates()[0].isClearState());
shouldThrow.set(true);
// Verify the lookup for this tenant
final OverdueConfig result2 = overdueConfigCache.getOverdueConfig(multiTenantContext);
Assert.assertEquals(result2, result);
// Verify the lookup with another context for the same tenant
final InternalCallContext sameMultiTenantContext = Mockito.mock(InternalCallContext.class);
Mockito.when(sameMultiTenantContext.getAccountRecordId()).thenReturn(9102L);
Mockito.when(sameMultiTenantContext.getTenantRecordId()).thenReturn(multiTenantRecordId);
Assert.assertEquals(overdueConfigCache.getOverdueConfig(sameMultiTenantContext), result);
// Verify the lookup with the other tenant
Assert.assertEquals(overdueConfigCache.getOverdueConfig(otherMultiTenantContext), otherResult);
}
use of org.killbill.billing.callcontext.InternalTenantContext in project killbill by killbill.
the class DefaultApiBase method toPaymentControlPluginNames.
protected List<String> toPaymentControlPluginNames(final PaymentOptions paymentOptions, final CallContext callContext) {
final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(callContext);
// Special path for JAX-RS InvoicePayment endpoints (see JaxRsResourceBase)
final List<String> controlPluginNames = paymentConfig.getPaymentControlPluginNames(internalTenantContext);
if (controlPluginNames != null && paymentOptions.getPaymentControlPluginNames() != null && paymentOptions.getPaymentControlPluginNames().size() == 1 && InvoicePaymentControlPluginApi.PLUGIN_NAME.equals(paymentOptions.getPaymentControlPluginNames().get(0))) {
final List<String> paymentControlPluginNames = new LinkedList<String>(paymentOptions.getPaymentControlPluginNames());
paymentControlPluginNames.addAll(controlPluginNames);
return paymentControlPluginNames;
} else if (paymentOptions.getPaymentControlPluginNames() != null && !paymentOptions.getPaymentControlPluginNames().isEmpty()) {
return paymentOptions.getPaymentControlPluginNames();
} else if (controlPluginNames != null && !controlPluginNames.isEmpty()) {
return controlPluginNames;
} else {
return ImmutableList.<String>of();
}
}
use of org.killbill.billing.callcontext.InternalTenantContext in project killbill by killbill.
the class EmailInvoiceNotifier method notify.
@Override
public void notify(final Account account, final Invoice invoice, final TenantContext context) throws InvoiceApiException {
if (Strings.emptyToNull(account.getEmail()) == null) {
throw new InvoiceApiException(new IllegalArgumentException("Email for account " + account.getId() + " not specified"), ErrorCode.EMAIL_SENDING_FAILED);
}
final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(account.getId(), context);
final List<String> to = new ArrayList<String>();
to.add(account.getEmail());
final List<AccountEmail> accountEmailList = accountApi.getEmails(account.getId(), internalTenantContext);
final List<String> cc = new ArrayList<String>();
for (final AccountEmail email : accountEmailList) {
cc.add(email.getEmail());
}
// Check if this account has the MANUAL_PAY system tag
boolean manualPay = false;
final List<Tag> accountTags = tagUserApi.getTags(account.getId(), ObjectType.ACCOUNT, internalTenantContext);
for (final Tag tag : accountTags) {
if (ControlTagType.MANUAL_PAY.getId().equals(tag.getTagDefinitionId())) {
manualPay = true;
break;
}
}
final HtmlInvoice htmlInvoice;
try {
htmlInvoice = generator.generateInvoice(account, invoice, manualPay, internalTenantContext);
} catch (final IOException e) {
throw new InvoiceApiException(e, ErrorCode.EMAIL_SENDING_FAILED);
}
// take localized subject, or the configured one if the localized one is not available
String subject = htmlInvoice.getSubject();
if (subject == null) {
subject = config.getInvoiceEmailSubject();
}
final EmailSender sender = new DefaultEmailSender(config);
try {
sender.sendHTMLEmail(to, cc, subject, htmlInvoice.getBody());
} catch (final EmailApiException e) {
throw new InvoiceApiException(e, ErrorCode.EMAIL_SENDING_FAILED);
} catch (final IOException e) {
throw new InvoiceApiException(e, ErrorCode.EMAIL_SENDING_FAILED);
}
}
use of org.killbill.billing.callcontext.InternalTenantContext in project killbill by killbill.
the class DefaultInvoiceUserApi method insertExternalCharges.
@Override
public List<InvoiceItem> insertExternalCharges(final UUID accountId, final LocalDate effectiveDate, final Iterable<InvoiceItem> charges, final boolean autoCommit, final CallContext context) throws InvoiceApiException {
for (final InvoiceItem charge : charges) {
if (charge.getAmount() == null || charge.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new InvoiceApiException(ErrorCode.EXTERNAL_CHARGE_AMOUNT_INVALID, charge.getAmount());
}
}
final WithAccountLock withAccountLock = new WithAccountLock() {
@Override
public Iterable<Invoice> prepareInvoices() throws InvoiceApiException {
final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(accountId, context);
final LocalDate invoiceDate = internalTenantContext.toLocalDate(context.getCreatedDate());
// Group all new external charges on the same invoice (per currency)
final Map<Currency, Invoice> newInvoicesForExternalCharges = new HashMap<Currency, Invoice>();
final Map<UUID, Invoice> existingInvoicesForExternalCharges = new HashMap<UUID, Invoice>();
for (final InvoiceItem charge : charges) {
final Invoice invoiceForExternalCharge;
final UUID invoiceIdForExternalCharge = charge.getInvoiceId();
// Create an invoice for that external charge if it doesn't exist
if (invoiceIdForExternalCharge == null) {
final Currency currency = charge.getCurrency();
if (newInvoicesForExternalCharges.get(currency) == null) {
final InvoiceStatus status = autoCommit ? InvoiceStatus.COMMITTED : InvoiceStatus.DRAFT;
final Invoice newInvoiceForExternalCharge = new DefaultInvoice(accountId, invoiceDate, effectiveDate, currency, status);
newInvoicesForExternalCharges.put(currency, newInvoiceForExternalCharge);
}
invoiceForExternalCharge = newInvoicesForExternalCharges.get(currency);
} else {
if (existingInvoicesForExternalCharges.get(invoiceIdForExternalCharge) == null) {
final Invoice existingInvoiceForExternalCharge = getInvoice(invoiceIdForExternalCharge, context);
existingInvoicesForExternalCharges.put(invoiceIdForExternalCharge, existingInvoiceForExternalCharge);
}
invoiceForExternalCharge = existingInvoicesForExternalCharges.get(invoiceIdForExternalCharge);
}
final InvoiceItem externalCharge = new ExternalChargeInvoiceItem(UUIDs.randomUUID(), context.getCreatedDate(), invoiceForExternalCharge.getId(), accountId, charge.getBundleId(), charge.getDescription(), effectiveDate, charge.getAmount(), charge.getCurrency());
invoiceForExternalCharge.addInvoiceItem(externalCharge);
}
return Iterables.<Invoice>concat(newInvoicesForExternalCharges.values(), existingInvoicesForExternalCharges.values());
}
};
return invoiceApiHelper.dispatchToInvoicePluginsAndInsertItems(accountId, false, withAccountLock, context);
}
use of org.killbill.billing.callcontext.InternalTenantContext in project killbill by killbill.
the class DefaultSubscriptionInternalApi method populateDryRunEvents.
private void populateDryRunEvents(@Nullable final UUID bundleId, @Nullable final DryRunArguments dryRunArguments, final Collection<SubscriptionBaseEvent> outputDryRunEvents, final Collection<SubscriptionBase> outputSubscriptions, final InternalTenantContext context) throws SubscriptionBaseApiException {
if (dryRunArguments == null || dryRunArguments.getAction() == null) {
return;
}
final DateTime utcNow = clock.getUTCNow();
List<SubscriptionBaseEvent> dryRunEvents = null;
try {
final PlanPhaseSpecifier inputSpec = dryRunArguments.getPlanPhaseSpecifier();
final boolean isInputSpecNullOrEmpty = inputSpec == null || (inputSpec.getPlanName() == null && inputSpec.getProductName() == null && inputSpec.getBillingPeriod() == null);
final Catalog catalog = catalogService.getFullCatalog(true, true, context);
// Create an overridesWithContext with a null context to indicate this is dryRun and no price overriden plan should be created.
final PlanPhasePriceOverridesWithCallContext overridesWithContext = new DefaultPlanPhasePriceOverridesWithCallContext(dryRunArguments.getPlanPhasePriceOverrides(), null);
final Plan plan = isInputSpecNullOrEmpty ? null : catalog.createOrFindPlan(inputSpec, overridesWithContext, utcNow);
final TenantContext tenantContext = internalCallContextFactory.createTenantContext(context);
switch(dryRunArguments.getAction()) {
case START_BILLING:
final DefaultSubscriptionBase baseSubscription = (DefaultSubscriptionBase) dao.getBaseSubscription(bundleId, context);
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, plan, inputSpec.getPhaseType(), plan.getPriceListName(), startEffectiveDate, utcNow, context);
final SubscriptionBuilder builder = new SubscriptionBuilder().setId(subscriptionId).setBundleId(bundleId).setBundleExternalKey(null).setCategory(plan.getProduct().getCategory()).setBundleStartDate(bundleStartDate).setAlignStartDate(startEffectiveDate);
final DefaultSubscriptionBase newSubscription = new DefaultSubscriptionBase(builder, apiService, clock);
newSubscription.rebuildTransitions(dryRunEvents, catalog);
outputSubscriptions.add(newSubscription);
break;
case CHANGE:
final DefaultSubscriptionBase subscriptionForChange = (DefaultSubscriptionBase) dao.getSubscriptionFromId(dryRunArguments.getSubscriptionId(), context);
DateTime changeEffectiveDate = dryRunArguments.getEffectiveDate() != null ? context.toUTCDateTime(dryRunArguments.getEffectiveDate()) : null;
if (changeEffectiveDate == null) {
BillingActionPolicy policy = dryRunArguments.getBillingActionPolicy();
if (policy == null) {
final PlanChangeResult planChangeResult = apiService.getPlanChangeResult(subscriptionForChange, inputSpec, utcNow, tenantContext);
policy = planChangeResult.getPolicy();
}
// We pass null for billingAlignment, accountTimezone, account BCD because this is not available which means that dryRun with START_OF_TERM BillingPolicy will fail
changeEffectiveDate = subscriptionForChange.getPlanChangeEffectiveDate(policy, null, null, -1, context);
}
dryRunEvents = apiService.getEventsOnChangePlan(subscriptionForChange, plan, plan.getPriceListName(), changeEffectiveDate, utcNow, true, context);
break;
case STOP_BILLING:
final DefaultSubscriptionBase subscriptionForCancellation = (DefaultSubscriptionBase) dao.getSubscriptionFromId(dryRunArguments.getSubscriptionId(), context);
DateTime cancelEffectiveDate = dryRunArguments.getEffectiveDate() != null ? context.toUTCDateTime(dryRunArguments.getEffectiveDate()) : null;
if (dryRunArguments.getEffectiveDate() == null) {
BillingActionPolicy policy = dryRunArguments.getBillingActionPolicy();
if (policy == null) {
final Plan currentPlan = subscriptionForCancellation.getCurrentPlan();
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(currentPlan.getName(), subscriptionForCancellation.getCurrentPhase().getPhaseType());
policy = catalogService.getFullCatalog(true, true, context).planCancelPolicy(spec, utcNow);
}
// We pass null for billingAlignment, accountTimezone, account BCD because this is not available which means that dryRun with START_OF_TERM BillingPolicy will fail
cancelEffectiveDate = subscriptionForCancellation.getPlanChangeEffectiveDate(policy, null, null, -1, context);
}
dryRunEvents = apiService.getEventsOnCancelPlan(subscriptionForCancellation, cancelEffectiveDate, utcNow, true, context);
break;
default:
throw new IllegalArgumentException("Unexpected dryRunArguments action " + dryRunArguments.getAction());
}
} catch (final CatalogApiException e) {
throw new SubscriptionBaseApiException(e);
}
if (dryRunEvents != null && !dryRunEvents.isEmpty()) {
outputDryRunEvents.addAll(dryRunEvents);
}
}
Aggregations