Search in sources :

Example 1 with ImmutableAccountData

use of org.killbill.billing.account.api.ImmutableAccountData in project killbill by killbill.

the class DefaultSubscriptionApi method addBlockingState.

@Override
public void addBlockingState(final BlockingState inputBlockingState, @Nullable final LocalDate inputEffectiveDate, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
    logAddBlockingState(log, inputBlockingState, inputEffectiveDate);
    // This is in no way an exhaustive arg validation, but to to ensure plugin would not hijack private entitlement state or service name
    if (inputBlockingState.getService() == null || inputBlockingState.getService().equals(EntitlementService.ENTITLEMENT_SERVICE_NAME)) {
        throw new EntitlementApiException(ErrorCode.SUB_BLOCKING_STATE_INVALID_ARG, "Need to specify a valid serviceName");
    }
    if (inputBlockingState.getStateName() == null || inputBlockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CANCELLED) || inputBlockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_BLOCKED) || inputBlockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CLEAR)) {
        throw new EntitlementApiException(ErrorCode.SUB_BLOCKING_STATE_INVALID_ARG, "Need to specify a valid stateName");
    }
    final InternalCallContext internalCallContextWithValidAccountId;
    final ImmutableAccountData account;
    final UUID accountId;
    final UUID bundleId;
    final String externalKey;
    try {
        switch(inputBlockingState.getType()) {
            case ACCOUNT:
                internalCallContextWithValidAccountId = internalCallContextFactory.createInternalCallContext(inputBlockingState.getBlockedId(), ObjectType.ACCOUNT, callContext);
                account = accountApi.getImmutableAccountDataById(inputBlockingState.getBlockedId(), internalCallContextWithValidAccountId);
                externalKey = account.getExternalKey();
                accountId = account.getId();
                bundleId = null;
                break;
            case SUBSCRIPTION_BUNDLE:
                internalCallContextWithValidAccountId = internalCallContextFactory.createInternalCallContext(inputBlockingState.getBlockedId(), ObjectType.BUNDLE, callContext);
                final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.getBundleFromId(inputBlockingState.getBlockedId(), internalCallContextWithValidAccountId);
                externalKey = bundle.getExternalKey();
                bundleId = bundle.getId();
                accountId = bundle.getAccountId();
                break;
            case SUBSCRIPTION:
                internalCallContextWithValidAccountId = internalCallContextFactory.createInternalCallContext(inputBlockingState.getBlockedId(), ObjectType.SUBSCRIPTION, callContext);
                final Entitlement entitlement = entitlementInternalApi.getEntitlementForId(inputBlockingState.getBlockedId(), internalCallContextWithValidAccountId);
                bundleId = entitlement.getBundleId();
                accountId = entitlement.getAccountId();
                externalKey = null;
                break;
            default:
                throw new IllegalStateException("Invalid blockingStateType " + inputBlockingState.getType());
        }
    } catch (final AccountApiException e) {
        throw new EntitlementApiException(e);
    } catch (final SubscriptionBaseApiException e) {
        throw new EntitlementApiException(e);
    }
    final DateTime effectiveDate = inputEffectiveDate == null ? clock.getUTCNow() : internalCallContextWithValidAccountId.toUTCDateTime(inputEffectiveDate);
    final DefaultBlockingState blockingState = new DefaultBlockingState(inputBlockingState, effectiveDate);
    final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = new DefaultBaseEntitlementWithAddOnsSpecifier(bundleId, externalKey, new ArrayList<EntitlementSpecifier>(), internalCallContextWithValidAccountId.toLocalDate(effectiveDate), internalCallContextWithValidAccountId.toLocalDate(effectiveDate), false);
    final List<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifierList = new ArrayList<BaseEntitlementWithAddOnsSpecifier>();
    baseEntitlementWithAddOnsSpecifierList.add(baseEntitlementWithAddOnsSpecifier);
    final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.ADD_BLOCKING_STATE, accountId, null, baseEntitlementWithAddOnsSpecifierList, null, properties, callContext);
    final WithEntitlementPlugin<Void> addBlockingStateWithPlugin = new WithEntitlementPlugin<Void>() {

        @Override
        public Void doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
            entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingState, internalCallContextWithValidAccountId);
            return null;
        }
    };
    pluginExecution.executeWithPlugin(addBlockingStateWithPlugin, pluginContext);
}
Also used : ImmutableAccountData(org.killbill.billing.account.api.ImmutableAccountData) WithEntitlementPlugin(org.killbill.billing.entitlement.api.EntitlementPluginExecution.WithEntitlementPlugin) ArrayList(java.util.ArrayList) InternalCallContext(org.killbill.billing.callcontext.InternalCallContext) DateTime(org.joda.time.DateTime) AccountApiException(org.killbill.billing.account.api.AccountApiException) SubscriptionBaseBundle(org.killbill.billing.subscription.api.user.SubscriptionBaseBundle) UUID(java.util.UUID) SubscriptionBaseApiException(org.killbill.billing.subscription.api.user.SubscriptionBaseApiException) DefaultBlockingState(org.killbill.billing.junction.DefaultBlockingState) EntitlementContext(org.killbill.billing.entitlement.plugin.api.EntitlementContext)

Example 2 with ImmutableAccountData

use of org.killbill.billing.account.api.ImmutableAccountData in project killbill by killbill.

the class EventsStreamBuilder method buildForAccount.

// Special signature for ProxyBlockingStateDao to save a DAO call
public AccountEventsStreams buildForAccount(final Map<UUID, List<SubscriptionBase>> subscriptions, final InternalTenantContext internalTenantContext) throws EntitlementApiException {
    // Retrieve the account
    final ImmutableAccountData account;
    try {
        account = accountInternalApi.getImmutableAccountDataByRecordId(internalTenantContext.getAccountRecordId(), internalTenantContext);
    } catch (AccountApiException e) {
        throw new EntitlementApiException(e);
    }
    if (subscriptions.isEmpty()) {
        // Bail early
        return new DefaultAccountEventsStreams(account);
    }
    // Retrieve the bundles
    final List<SubscriptionBaseBundle> bundles = subscriptionInternalApi.getBundlesForAccount(account.getId(), internalTenantContext);
    // Map bundle id -> bundles
    final Map<UUID, SubscriptionBaseBundle> bundlesPerId = new HashMap<UUID, SubscriptionBaseBundle>();
    for (final SubscriptionBaseBundle bundle : bundles) {
        bundlesPerId.put(bundle.getId(), bundle);
    }
    // Retrieve the blocking states
    final List<BlockingState> blockingStatesForAccount = defaultBlockingStateDao.getBlockingAllForAccountRecordId(internalTenantContext);
    // Optimization: build lookup tables for blocking states states
    final Collection<BlockingState> accountBlockingStates = new LinkedList<BlockingState>();
    final Map<UUID, List<BlockingState>> blockingStatesPerSubscription = new HashMap<UUID, List<BlockingState>>();
    final Map<UUID, List<BlockingState>> blockingStatesPerBundle = new HashMap<UUID, List<BlockingState>>();
    for (final BlockingState blockingState : blockingStatesForAccount) {
        if (BlockingStateType.SUBSCRIPTION.equals(blockingState.getType())) {
            if (blockingStatesPerSubscription.get(blockingState.getBlockedId()) == null) {
                blockingStatesPerSubscription.put(blockingState.getBlockedId(), new LinkedList<BlockingState>());
            }
            blockingStatesPerSubscription.get(blockingState.getBlockedId()).add(blockingState);
        } else if (BlockingStateType.SUBSCRIPTION_BUNDLE.equals(blockingState.getType())) {
            if (blockingStatesPerBundle.get(blockingState.getBlockedId()) == null) {
                blockingStatesPerBundle.put(blockingState.getBlockedId(), new LinkedList<BlockingState>());
            }
            blockingStatesPerBundle.get(blockingState.getBlockedId()).add(blockingState);
        } else if (BlockingStateType.ACCOUNT.equals(blockingState.getType()) && account.getId().equals(blockingState.getBlockedId())) {
            accountBlockingStates.add(blockingState);
        }
    }
    // Build the EventsStream objects
    final Map<UUID, Integer> bcdCache = new HashMap<UUID, Integer>();
    final Map<UUID, Collection<EventsStream>> entitlementsPerBundle = new HashMap<UUID, Collection<EventsStream>>();
    for (final UUID bundleId : subscriptions.keySet()) {
        final SubscriptionBaseBundle bundle = bundlesPerId.get(bundleId);
        final List<SubscriptionBase> allSubscriptionsForBundle = subscriptions.get(bundleId);
        final SubscriptionBase baseSubscription = findBaseSubscription(allSubscriptionsForBundle);
        final List<BlockingState> bundleBlockingStates = MoreObjects.firstNonNull(blockingStatesPerBundle.get(bundleId), ImmutableList.<BlockingState>of());
        if (entitlementsPerBundle.get(bundleId) == null) {
            entitlementsPerBundle.put(bundleId, new LinkedList<EventsStream>());
        }
        for (final SubscriptionBase subscription : allSubscriptionsForBundle) {
            final List<BlockingState> subscriptionBlockingStatesOnDisk = MoreObjects.firstNonNull(blockingStatesPerSubscription.get(subscription.getId()), ImmutableList.<BlockingState>of());
            // We cannot always use blockingStatesForAccount here: we need subscriptionBlockingStates to contain the events not on disk when building an EventsStream
            // for an add-on - which means going through the magic of ProxyBlockingStateDao, which will recursively
            // create EventsStream objects. To avoid an infinite recursion, bypass ProxyBlockingStateDao when it's not
            // needed, i.e. if this EventStream is for a standalone or a base subscription
            final List<BlockingState> subscriptionBlockingStates;
            if (baseSubscription == null || subscription.getId().equals(baseSubscription.getId())) {
                subscriptionBlockingStates = subscriptionBlockingStatesOnDisk;
            } else {
                subscriptionBlockingStates = blockingStateDao.getBlockingHistory(subscriptionBlockingStatesOnDisk, blockingStatesForAccount, account, bundle, baseSubscription, subscription, allSubscriptionsForBundle, internalTenantContext);
            }
            // Merge the BlockingStates
            final Collection<BlockingState> blockingStateSet = new LinkedHashSet<BlockingState>(accountBlockingStates);
            blockingStateSet.addAll(bundleBlockingStates);
            blockingStateSet.addAll(subscriptionBlockingStates);
            final List<BlockingState> blockingStates = ProxyBlockingStateDao.sortedCopy(blockingStateSet);
            final EventsStream eventStream = buildForEntitlement(account, bundle, baseSubscription, subscription, allSubscriptionsForBundle, blockingStates, bcdCache, internalTenantContext);
            entitlementsPerBundle.get(bundleId).add(eventStream);
        }
    }
    return new DefaultAccountEventsStreams(account, bundles, entitlementsPerBundle);
}
Also used : LinkedHashSet(java.util.LinkedHashSet) ImmutableAccountData(org.killbill.billing.account.api.ImmutableAccountData) HashMap(java.util.HashMap) EventsStream(org.killbill.billing.entitlement.EventsStream) BlockingState(org.killbill.billing.entitlement.api.BlockingState) SubscriptionBase(org.killbill.billing.subscription.api.SubscriptionBase) AccountApiException(org.killbill.billing.account.api.AccountApiException) SubscriptionBaseBundle(org.killbill.billing.subscription.api.user.SubscriptionBaseBundle) List(java.util.List) ImmutableList(com.google.common.collect.ImmutableList) LinkedList(java.util.LinkedList) DefaultAccountEventsStreams(org.killbill.billing.entitlement.api.svcs.DefaultAccountEventsStreams) UUID(java.util.UUID) EntitlementApiException(org.killbill.billing.entitlement.api.EntitlementApiException) LinkedList(java.util.LinkedList) Collection(java.util.Collection)

Example 3 with ImmutableAccountData

use of org.killbill.billing.account.api.ImmutableAccountData in project killbill by killbill.

the class InvoiceDispatcher method processAccountWithLockAndInputTargetDate.

private Invoice processAccountWithLockAndInputTargetDate(final UUID accountId, final LocalDate targetDate, final BillingEventSet billingEvents, final boolean isDryRun, final InternalCallContext context) throws InvoiceApiException {
    try {
        final ImmutableAccountData account = accountApi.getImmutableAccountDataById(accountId, context);
        final List<Invoice> invoices = billingEvents.isAccountAutoInvoiceOff() ? ImmutableList.<Invoice>of() : ImmutableList.<Invoice>copyOf(Collections2.transform(invoiceDao.getInvoicesByAccount(context), new Function<InvoiceModelDao, Invoice>() {

            @Override
            public Invoice apply(final InvoiceModelDao input) {
                return new DefaultInvoice(input);
            }
        }));
        final Currency targetCurrency = account.getCurrency();
        final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, billingEvents, invoices, targetDate, targetCurrency, context);
        final DefaultInvoice invoice = invoiceWithMetadata.getInvoice();
        // Compute future notifications
        final FutureAccountNotifications futureAccountNotifications = createNextFutureNotificationDate(invoiceWithMetadata, context);
        //
        if (invoice == null) {
            if (isDryRun) {
                log.info("Generated null dryRun invoice for accountId='{}', targetDate='{}'", accountId, targetDate);
            } else {
                log.info("Generated null invoice for accountId='{}', targetDate='{}'", accountId, targetDate);
                final BusInternalEvent event = new DefaultNullInvoiceEvent(accountId, clock.getUTCToday(), context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
                commitInvoiceAndSetFutureNotifications(account, null, futureAccountNotifications, context);
                postEvent(event);
            }
            return null;
        }
        // Generate missing credit (> 0 for generation and < 0 for use) prior we call the plugin
        final InvoiceItem cbaItemPreInvoicePlugins = computeCBAOnExistingInvoice(invoice, context);
        DefaultInvoice tmpInvoiceForInvoicePlugins = invoice;
        if (cbaItemPreInvoicePlugins != null) {
            tmpInvoiceForInvoicePlugins = (DefaultInvoice) tmpInvoiceForInvoicePlugins.clone();
            tmpInvoiceForInvoicePlugins.addInvoiceItem(cbaItemPreInvoicePlugins);
        }
        //
        // Ask external invoice plugins if additional items (tax, etc) shall be added to the invoice
        //
        final CallContext callContext = buildCallContext(context);
        final List<InvoiceItem> additionalInvoiceItemsFromPlugins = invoicePluginDispatcher.getAdditionalInvoiceItems(tmpInvoiceForInvoicePlugins, isDryRun, callContext);
        if (additionalInvoiceItemsFromPlugins.isEmpty()) {
            // PERF: avoid re-computing the CBA if no change was made
            if (cbaItemPreInvoicePlugins != null) {
                invoice.addInvoiceItem(cbaItemPreInvoicePlugins);
            }
        } else {
            invoice.addInvoiceItems(additionalInvoiceItemsFromPlugins);
            // Use credit after we call the plugin (https://github.com/killbill/killbill/issues/637)
            final InvoiceItem cbaItemPostInvoicePlugins = computeCBAOnExistingInvoice(invoice, context);
            if (cbaItemPostInvoicePlugins != null) {
                invoice.addInvoiceItem(cbaItemPostInvoicePlugins);
            }
        }
        if (!isDryRun) {
            // Compute whether this is a new invoice object (or just some adjustments on an existing invoice), and extract invoiceIds for later use
            final Set<UUID> uniqueInvoiceIds = getUniqueInvoiceIds(invoice);
            final boolean isRealInvoiceWithItems = uniqueInvoiceIds.remove(invoice.getId());
            final Set<UUID> adjustedUniqueOtherInvoiceId = uniqueInvoiceIds;
            logInvoiceWithItems(account, invoice, targetDate, adjustedUniqueOtherInvoiceId, isRealInvoiceWithItems);
            // Transformation to Invoice -> InvoiceModelDao
            final InvoiceModelDao invoiceModelDao = new InvoiceModelDao(invoice);
            final List<InvoiceItemModelDao> invoiceItemModelDaos = transformToInvoiceModelDao(invoice.getInvoiceItems());
            invoiceModelDao.addInvoiceItems(invoiceItemModelDaos);
            // Commit invoice on disk
            final boolean isThereAnyItemsLeft = commitInvoiceAndSetFutureNotifications(account, invoiceModelDao, futureAccountNotifications, context);
            final boolean isRealInvoiceWithNonEmptyItems = isThereAnyItemsLeft ? isRealInvoiceWithItems : false;
            setChargedThroughDates(invoice.getInvoiceItems(FixedPriceInvoiceItem.class), invoice.getInvoiceItems(RecurringInvoiceItem.class), context);
            if (InvoiceStatus.COMMITTED.equals(invoice.getStatus())) {
                notifyAccountIfEnabled(account, invoice, isRealInvoiceWithNonEmptyItems, context);
            }
        }
        return invoice;
    } catch (final AccountApiException e) {
        log.error("Failed handling SubscriptionBase change.", e);
        return null;
    } catch (final SubscriptionBaseApiException e) {
        log.error("Failed handling SubscriptionBase change.", e);
        return null;
    }
}
Also used : ImmutableAccountData(org.killbill.billing.account.api.ImmutableAccountData) DefaultInvoice(org.killbill.billing.invoice.model.DefaultInvoice) Invoice(org.killbill.billing.invoice.api.Invoice) RecurringInvoiceItem(org.killbill.billing.invoice.model.RecurringInvoiceItem) InvoiceItem(org.killbill.billing.invoice.api.InvoiceItem) ItemAdjInvoiceItem(org.killbill.billing.invoice.model.ItemAdjInvoiceItem) FixedPriceInvoiceItem(org.killbill.billing.invoice.model.FixedPriceInvoiceItem) ParentInvoiceItem(org.killbill.billing.invoice.model.ParentInvoiceItem) RecurringInvoiceItem(org.killbill.billing.invoice.model.RecurringInvoiceItem) InvoiceModelDao(org.killbill.billing.invoice.dao.InvoiceModelDao) FixedPriceInvoiceItem(org.killbill.billing.invoice.model.FixedPriceInvoiceItem) BusInternalEvent(org.killbill.billing.events.BusInternalEvent) InvoiceWithMetadata(org.killbill.billing.invoice.generator.InvoiceWithMetadata) CallContext(org.killbill.billing.util.callcontext.CallContext) InternalCallContext(org.killbill.billing.callcontext.InternalCallContext) DefaultNullInvoiceEvent(org.killbill.billing.invoice.api.user.DefaultNullInvoiceEvent) InvoiceItemModelDao(org.killbill.billing.invoice.dao.InvoiceItemModelDao) Currency(org.killbill.billing.catalog.api.Currency) AccountApiException(org.killbill.billing.account.api.AccountApiException) UUID(java.util.UUID) PlanPhasePriceOverride(org.killbill.billing.catalog.api.PlanPhasePriceOverride) DefaultInvoice(org.killbill.billing.invoice.model.DefaultInvoice) SubscriptionBaseApiException(org.killbill.billing.subscription.api.user.SubscriptionBaseApiException)

Example 4 with ImmutableAccountData

use of org.killbill.billing.account.api.ImmutableAccountData in project killbill by killbill.

the class DefaultInternalBillingApi method getBillingEventsForAccountAndUpdateAccountBCD.

@Override
public BillingEventSet getBillingEventsForAccountAndUpdateAccountBCD(final UUID accountId, final DryRunArguments dryRunArguments, final InternalCallContext context) throws CatalogApiException, AccountApiException, SubscriptionBaseApiException {
    final StaticCatalog currentCatalog = catalogService.getCurrentCatalog(true, true, context);
    // Check to see if billing is off for the account
    final List<Tag> accountTags = tagApi.getTags(accountId, ObjectType.ACCOUNT, context);
    final boolean found_AUTO_INVOICING_OFF = is_AUTO_INVOICING_OFF(accountTags);
    final Set<UUID> skippedSubscriptions = new HashSet<UUID>();
    final DefaultBillingEventSet result;
    if (found_AUTO_INVOICING_OFF) {
        // billing is off, we are done
        result = new DefaultBillingEventSet(true, currentCatalog.getRecurringBillingMode());
    } else {
        final List<SubscriptionBaseBundle> bundles = subscriptionApi.getBundlesForAccount(accountId, context);
        final ImmutableAccountData account = accountApi.getImmutableAccountDataById(accountId, context);
        result = new DefaultBillingEventSet(false, currentCatalog.getRecurringBillingMode());
        addBillingEventsForBundles(bundles, account, dryRunArguments, context, result, skippedSubscriptions);
    }
    if (result.isEmpty()) {
        log.info("No billing event for accountId='{}'", accountId);
        return result;
    }
    // Pretty-print the events, before and after the blocking calculator does its magic
    final StringBuilder logStringBuilder = new StringBuilder("Computed billing events for accountId='").append(accountId).append("'");
    eventsToString(logStringBuilder, result);
    if (blockCalculator.insertBlockingEvents(result, skippedSubscriptions, context)) {
        logStringBuilder.append("\nBilling Events After Blocking");
        eventsToString(logStringBuilder, result);
    }
    log.info(logStringBuilder.toString());
    return result;
}
Also used : ImmutableAccountData(org.killbill.billing.account.api.ImmutableAccountData) SubscriptionBaseBundle(org.killbill.billing.subscription.api.user.SubscriptionBaseBundle) Tag(org.killbill.billing.util.tag.Tag) UUID(java.util.UUID) StaticCatalog(org.killbill.billing.catalog.api.StaticCatalog) HashSet(java.util.HashSet)

Example 5 with ImmutableAccountData

use of org.killbill.billing.account.api.ImmutableAccountData in project killbill by killbill.

the class InternalCallContextFactory method getFixedOffsetTimeZone.

private DateTimeZone getFixedOffsetTimeZone(@Nullable final Long accountRecordId, final Long tenantRecordId) {
    if (accountRecordId == null || accountInternalApi == null) {
        return null;
    }
    populateMDCContext(accountRecordId, tenantRecordId);
    final ImmutableAccountData immutableAccountData = getImmutableAccountData(accountRecordId, tenantRecordId);
    // Will be null while creating the account
    return immutableAccountData == null ? null : immutableAccountData.getFixedOffsetTimeZone();
}
Also used : ImmutableAccountData(org.killbill.billing.account.api.ImmutableAccountData)

Aggregations

ImmutableAccountData (org.killbill.billing.account.api.ImmutableAccountData)14 UUID (java.util.UUID)7 AccountApiException (org.killbill.billing.account.api.AccountApiException)7 SubscriptionBaseBundle (org.killbill.billing.subscription.api.user.SubscriptionBaseBundle)6 ArrayList (java.util.ArrayList)4 SubscriptionBase (org.killbill.billing.subscription.api.SubscriptionBase)4 SubscriptionBaseApiException (org.killbill.billing.subscription.api.user.SubscriptionBaseApiException)4 LocalDate (org.joda.time.LocalDate)3 InternalCallContext (org.killbill.billing.callcontext.InternalCallContext)3 HashMap (java.util.HashMap)2 DateTime (org.joda.time.DateTime)2 InternalTenantContext (org.killbill.billing.callcontext.InternalTenantContext)2 BlockingState (org.killbill.billing.entitlement.api.BlockingState)2 EntitlementApiException (org.killbill.billing.entitlement.api.EntitlementApiException)2 WithEntitlementPlugin (org.killbill.billing.entitlement.api.EntitlementPluginExecution.WithEntitlementPlugin)2 EntitlementContext (org.killbill.billing.entitlement.plugin.api.EntitlementContext)2 Invoice (org.killbill.billing.invoice.api.Invoice)2 DefaultBlockingState (org.killbill.billing.junction.DefaultBlockingState)2 Test (org.testng.annotations.Test)2 Predicate (com.google.common.base.Predicate)1