@Override public synchronized void setBlockingState( final BlockingState state, final Clock clock, final InternalCallContext context) { if (blockingStates.get(state.getBlockedId()) == null) { blockingStates.put(state.getBlockedId(), new ArrayList<BlockingState>()); } blockingStates.get(state.getBlockedId()).add(state); if (blockingStatesPerAccountRecordId.get(context.getAccountRecordId()) == null) { blockingStatesPerAccountRecordId.put( context.getAccountRecordId(), new ArrayList<BlockingState>()); } blockingStatesPerAccountRecordId.get(context.getAccountRecordId()).add(state); }
/** * Create an adjustment for a given invoice item. This just creates the object in memory, it * doesn't write it to disk. * * @param invoiceToBeAdjusted the invoice * @param invoiceItemId the invoice item id to adjust * @param positiveAdjAmount the amount to adjust. Pass null to adjust the full amount of the * original item * @param currency the currency of the amount. Pass null to default to the original currency used * @param effectiveDate adjustment effective date, in the account timezone * @return the adjustment item */ public InvoiceItem createAdjustmentItem( final Invoice invoiceToBeAdjusted, final UUID invoiceItemId, @Nullable final BigDecimal positiveAdjAmount, @Nullable final Currency currency, final LocalDate effectiveDate, final InternalCallContext context) throws InvoiceApiException { final InvoiceItem invoiceItemToBeAdjusted = Iterables.<InvoiceItem>tryFind( invoiceToBeAdjusted.getInvoiceItems(), new Predicate<InvoiceItem>() { @Override public boolean apply(final InvoiceItem input) { return input.getId().equals(invoiceItemId); } }) .orNull(); if (invoiceItemToBeAdjusted == null) { throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_NOT_FOUND, invoiceItemId); } // Check the specified currency matches the one of the existing invoice final Currency currencyForAdjustment = Objects.firstNonNull(currency, invoiceItemToBeAdjusted.getCurrency()); if (invoiceItemToBeAdjusted.getCurrency() != currencyForAdjustment) { throw new InvoiceApiException( ErrorCode.CURRENCY_INVALID, currency, invoiceItemToBeAdjusted.getCurrency()); } // Reuse the same logic we have for refund with item adjustment final Map<UUID, BigDecimal> input = new HashMap<UUID, BigDecimal>(); input.put(invoiceItemId, positiveAdjAmount); final Map<UUID, BigDecimal> output = dao.computeItemAdjustments(invoiceToBeAdjusted.getId().toString(), input, context); // If we pass that stage, it means the validation succeeded so we just need to extract resulting // amount and negate the result. final BigDecimal amountToAdjust = output.get(invoiceItemId).negate(); // Finally, create the adjustment return new ItemAdjInvoiceItem( UUIDs.randomUUID(), context.getCreatedDate(), invoiceItemToBeAdjusted.getInvoiceId(), invoiceItemToBeAdjusted.getAccountId(), effectiveDate, null, amountToAdjust, currencyForAdjustment, invoiceItemToBeAdjusted.getId()); }
@Override protected void postBusEventFromTransaction( final AccountModelDao account, final AccountModelDao savedAccount, final ChangeType changeType, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context) throws BillingExceptionBase { // This is only called for the create call (see update below) switch (changeType) { case INSERT: break; default: return; } final Long recordId = entitySqlDaoWrapperFactory .become(AccountSqlDao.class) .getRecordId(savedAccount.getId().toString(), context); // We need to re-hydrate the callcontext with the account record id final InternalCallContext rehydratedContext = internalCallContextFactory.createInternalCallContext(recordId, context); final AccountCreationInternalEvent creationEvent = new DefaultAccountCreationEvent( new DefaultAccountData(savedAccount), savedAccount.getId(), rehydratedContext.getAccountRecordId(), rehydratedContext.getTenantRecordId(), rehydratedContext.getUserToken()); try { eventBus.postFromTransaction( creationEvent, entitySqlDaoWrapperFactory.getHandle().getConnection()); } catch (final EventBusException e) { log.warn("Failed to post account creation event for accountId='{}'", savedAccount.getId(), e); } }
public RawUsageOptimizerResult getConsumableInArrearUsage( final LocalDate firstEventStartDate, final LocalDate targetDate, final Iterable<InvoiceItem> existingUsageItems, final Map<String, Usage> knownUsage, final InternalCallContext internalCallContext) { final LocalDate targetStartDate = config.getMaxRawUsagePreviousPeriod() > 0 ? getOptimizedRawUsageStartDate( firstEventStartDate, targetDate, existingUsageItems, knownUsage) : firstEventStartDate; log.info( "RawUsageOptimizer [accountRecordId = {}]: rawUsageStartDate = {}, (proposed) firstEventStartDate = {}", new Object[] { internalCallContext.getAccountRecordId(), targetStartDate, firstEventStartDate }); final List<RawUsage> rawUsageData = usageApi.getRawUsageForAccount(targetStartDate, targetDate, internalCallContext); return new RawUsageOptimizerResult(firstEventStartDate, targetStartDate, rawUsageData); }
@Override public SubscriptionBase createSubscription( final UUID bundleId, final PlanPhaseSpecifier spec, final DateTime requestedDateWithMs, final InternalCallContext context) throws SubscriptionBaseApiException { try { final String realPriceList = (spec.getPriceListName() == null) ? PriceListSet.DEFAULT_PRICELIST_NAME : spec.getPriceListName(); final DateTime now = clock.getUTCNow(); final DateTime requestedDate = (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : now; if (requestedDate.isAfter(now)) { throw new SubscriptionBaseApiException( ErrorCode.SUB_INVALID_REQUESTED_DATE, now.toString(), requestedDate.toString()); } final DateTime effectiveDate = requestedDate; final Catalog catalog = catalogService.getFullCatalog(); final Plan plan = catalog.findPlan( spec.getProductName(), spec.getBillingPeriod(), realPriceList, requestedDate); final PlanPhase phase = plan.getAllPhases()[0]; if (phase == null) { throw new SubscriptionBaseError( String.format( "No initial PlanPhase for Product %s, term %s and set %s does not exist in the catalog", spec.getProductName(), spec.getBillingPeriod().toString(), realPriceList)); } final SubscriptionBaseBundle bundle = dao.getSubscriptionBundleFromId(bundleId, context); if (bundle == null) { throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_NO_BUNDLE, bundleId); } DateTime bundleStartDate = null; final DefaultSubscriptionBase baseSubscription = (DefaultSubscriptionBase) dao.getBaseSubscription(bundleId, context); switch (plan.getProduct().getCategory()) { case BASE: if (baseSubscription != null) { if (baseSubscription.getState() == EntitlementState.ACTIVE) { throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_BP_EXISTS, bundleId); } } bundleStartDate = requestedDate; break; case ADD_ON: if (baseSubscription == null) { throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_NO_BP, bundleId); } if (effectiveDate.isBefore(baseSubscription.getStartDate())) { throw new SubscriptionBaseApiException( ErrorCode.SUB_INVALID_REQUESTED_DATE, effectiveDate.toString(), baseSubscription.getStartDate().toString()); } addonUtils.checkAddonCreationRights(baseSubscription, plan); bundleStartDate = baseSubscription.getStartDate(); break; case STANDALONE: if (baseSubscription != null) { throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_BP_EXISTS, bundleId); } // Not really but we don't care, there is no alignment for STANDALONE subscriptions bundleStartDate = requestedDate; break; default: throw new SubscriptionBaseError( String.format( "Can't create subscription of type %s", plan.getProduct().getCategory().toString())); } final UUID tenantId = nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT); return apiService.createPlan( new SubscriptionBuilder() .setId(UUID.randomUUID()) .setBundleId(bundleId) .setCategory(plan.getProduct().getCategory()) .setBundleStartDate(bundleStartDate) .setAlignStartDate(effectiveDate), plan, spec.getPhaseType(), realPriceList, requestedDate, effectiveDate, now, context.toCallContext(tenantId)); } catch (CatalogApiException e) { throw new SubscriptionBaseApiException(e); } }