use of org.killbill.billing.util.api.TagApiException in project killbill by killbill.
the class InvoiceDispatcher method processAccountWithLock.
private Invoice processAccountWithLock(final boolean parkedAccount, final UUID accountId, @Nullable final LocalDate inputTargetDateMaybeNull, @Nullable final DryRunArguments dryRunArguments, final InternalCallContext context) throws InvoiceApiException {
final boolean isDryRun = dryRunArguments != null;
final boolean upcomingInvoiceDryRun = isDryRun && DryRunType.UPCOMING_INVOICE.equals(dryRunArguments.getDryRunType());
LocalDate inputTargetDate = inputTargetDateMaybeNull;
// A null inputTargetDate is only allowed in dryRun mode to have the system compute it
if (inputTargetDate == null && !upcomingInvoiceDryRun) {
inputTargetDate = clock.getUTCToday();
}
Preconditions.checkArgument(inputTargetDate != null || upcomingInvoiceDryRun, "inputTargetDate is required in non dryRun mode");
try {
// Make sure to first set the BCD if needed then get the account object (to have the BCD set)
final BillingEventSet billingEvents = billingApi.getBillingEventsForAccountAndUpdateAccountBCD(accountId, dryRunArguments, context);
if (billingEvents.isEmpty()) {
return null;
}
final Iterable<UUID> filteredSubscriptionIdsForDryRun = getFilteredSubscriptionIdsForDryRun(dryRunArguments, billingEvents);
final List<LocalDate> candidateTargetDates = (inputTargetDate != null) ? ImmutableList.<LocalDate>of(inputTargetDate) : getUpcomingInvoiceCandidateDates(filteredSubscriptionIdsForDryRun, context);
for (final LocalDate curTargetDate : candidateTargetDates) {
final Invoice invoice = processAccountWithLockAndInputTargetDate(accountId, curTargetDate, billingEvents, isDryRun, context);
if (invoice != null) {
filterInvoiceItemsForDryRun(filteredSubscriptionIdsForDryRun, invoice);
if (!isDryRun && parkedAccount) {
try {
log.info("Illegal invoicing state fixed for accountId='{}', unparking account", accountId);
parkedAccountsManager.unparkAccount(accountId, context);
} catch (final TagApiException ignored) {
log.warn("Unable to unpark account", ignored);
}
}
return invoice;
}
}
return null;
} catch (final CatalogApiException e) {
log.warn("Failed to retrieve BillingEvents for accountId='{}', dryRunArguments='{}'", accountId, dryRunArguments, e);
return null;
} catch (final AccountApiException e) {
log.warn("Failed to retrieve BillingEvents for accountId='{}', dryRunArguments='{}'", accountId, dryRunArguments, e);
return null;
} catch (final SubscriptionBaseApiException e) {
log.warn("Failed to retrieve BillingEvents for accountId='{}', dryRunArguments='{}'", accountId, dryRunArguments, e);
return null;
} catch (final InvoiceApiException e) {
if (e.getCode() == ErrorCode.UNEXPECTED_ERROR.getCode() && !isDryRun) {
log.warn("Illegal invoicing state detected for accountId='{}', dryRunArguments='{}', parking account", accountId, dryRunArguments, e);
parkAccount(accountId, context);
}
throw e;
}
}
use of org.killbill.billing.util.api.TagApiException in project killbill by killbill.
the class TestDefaultTagDao method testCatchEventsOnCreateAndDelete.
@Test(groups = "slow")
public void testCatchEventsOnCreateAndDelete() throws Exception {
final String definitionName = UUID.randomUUID().toString().substring(0, 5);
final String description = UUID.randomUUID().toString().substring(0, 5);
final UUID objectId = UUID.randomUUID();
final ObjectType objectType = ObjectType.INVOICE_ITEM;
// Create a tag definition
eventsListener.pushExpectedEvent(NextEvent.TAG_DEFINITION);
final TagDefinitionModelDao createdTagDefinition = tagDefinitionDao.create(definitionName, description, ObjectType.ACCOUNT.name(), internalCallContext);
Assert.assertEquals(createdTagDefinition.getName(), definitionName);
Assert.assertEquals(createdTagDefinition.getDescription(), description);
assertListenerStatus();
final List<AuditLog> auditLogs1 = auditDao.getAuditLogsForId(TableName.TAG_DEFINITIONS, createdTagDefinition.getId(), AuditLevel.FULL, internalCallContext);
Assert.assertEquals(auditLogs1.size(), 1);
Assert.assertEquals(auditLogs1.get(0).getChangeType(), ChangeType.INSERT);
// Make sure we can create a tag
eventsListener.pushExpectedEvent(NextEvent.TAG);
final Tag tag = new DescriptiveTag(createdTagDefinition.getId(), objectType, objectId, internalCallContext.getCreatedDate());
tagDao.create(new TagModelDao(tag), internalCallContext);
assertListenerStatus();
final List<AuditLog> auditLogs2 = auditDao.getAuditLogsForId(TableName.TAG, tag.getId(), AuditLevel.FULL, internalCallContext);
Assert.assertEquals(auditLogs2.size(), 1);
Assert.assertEquals(auditLogs2.get(0).getChangeType(), ChangeType.INSERT);
// Make sure we can retrieve it via the DAO
final List<TagModelDao> foundTags = tagDao.getTagsForObject(objectId, objectType, false, internalCallContext);
Assert.assertEquals(foundTags.size(), 1);
Assert.assertEquals(foundTags.get(0).getTagDefinitionId(), createdTagDefinition.getId());
final List<TagModelDao> foundTagsForAccount = tagDao.getTagsForAccount(false, internalCallContext);
Assert.assertEquals(foundTagsForAccount.size(), 1);
Assert.assertEquals(foundTagsForAccount.get(0).getTagDefinitionId(), createdTagDefinition.getId());
// Delete the tag
eventsListener.pushExpectedEvent(NextEvent.TAG);
tagDao.deleteTag(objectId, objectType, createdTagDefinition.getId(), internalCallContext);
assertListenerStatus();
final List<AuditLog> auditLogs3 = auditDao.getAuditLogsForId(TableName.TAG, tag.getId(), AuditLevel.FULL, internalCallContext);
Assert.assertEquals(auditLogs3.size(), 2);
Assert.assertEquals(auditLogs3.get(0).getChangeType(), ChangeType.INSERT);
Assert.assertEquals(auditLogs3.get(1).getChangeType(), ChangeType.DELETE);
// Make sure the tag is deleted
Assert.assertEquals(tagDao.getTagsForObject(objectId, objectType, false, internalCallContext).size(), 0);
Assert.assertEquals(tagDao.getTagsForAccount(false, internalCallContext).size(), 0);
Assert.assertEquals(tagDao.getTagsForObject(objectId, objectType, true, internalCallContext).size(), 1);
Assert.assertEquals(tagDao.getTagsForAccount(true, internalCallContext).size(), 1);
// Delete tag again, check correct error
try {
tagDao.deleteTag(objectId, objectType, createdTagDefinition.getId(), internalCallContext);
Assert.fail("Deleting same tag again should fail");
} catch (final TagApiException e) {
Assert.assertEquals(e.getCode(), ErrorCode.TAG_DOES_NOT_EXIST.getCode());
}
}
use of org.killbill.billing.util.api.TagApiException in project killbill by killbill.
the class TestDefaultTagDao method testInsertMultipleTags.
@Test(groups = "slow")
public void testInsertMultipleTags() throws TagApiException {
final UUID objectId = UUID.randomUUID();
final ObjectType objectType = ObjectType.ACCOUNT;
eventsListener.pushExpectedEvent(NextEvent.TAG);
final Tag tag = new DescriptiveTag(ControlTagType.AUTO_INVOICING_OFF.getId(), objectType, objectId, internalCallContext.getCreatedDate());
tagDao.create(new TagModelDao(tag), internalCallContext);
assertListenerStatus();
try {
final Tag tag2 = new DescriptiveTag(ControlTagType.AUTO_INVOICING_OFF.getId(), objectType, objectId, internalCallContext.getCreatedDate());
tagDao.create(new TagModelDao(tag2), internalCallContext);
Assert.fail("Should not be able to create twice the same tag");
assertListenerStatus();
} catch (final TagApiException e) {
Assert.assertEquals(ErrorCode.TAG_ALREADY_EXISTS.getCode(), e.getCode());
}
}
use of org.killbill.billing.util.api.TagApiException in project killbill by killbill.
the class InvoiceDispatcher method processAccountInternal.
private Invoice processAccountInternal(final boolean isApiCall, final boolean parkedAccount, final UUID accountId, @Nullable final LocalDate inputTargetDateMaybeNull, @Nullable final DryRunArguments dryRunArguments, final boolean isRescheduled, final InternalCallContext context) throws InvoiceApiException {
final boolean isDryRun = dryRunArguments != null;
final boolean upcomingInvoiceDryRun = isDryRun && DryRunType.UPCOMING_INVOICE.equals(dryRunArguments.getDryRunType());
LocalDate inputTargetDate = inputTargetDateMaybeNull;
// A null inputTargetDate is only allowed in UPCOMING_INVOICE dryRun mode to have the system compute it
if (inputTargetDate == null && !upcomingInvoiceDryRun) {
inputTargetDate = context.toLocalDate(clock.getUTCNow());
}
Preconditions.checkArgument(inputTargetDate != null || upcomingInvoiceDryRun, "inputTargetDate is required in non dryRun mode");
// Passed through invoice code to be propagated to usage module/plugins
final LocalDate dryRunInfoDate = isDryRun && dryRunArguments.getDryRunType() == DryRunType.SUBSCRIPTION_ACTION ? dryRunArguments.getEffectiveDate() : inputTargetDate;
final DryRunInfo dryRunInfo = isDryRun ? new DryRunInfo(dryRunArguments.getDryRunType(), dryRunInfoDate) : null;
final Map<InvoiceTiming, Long> invoiceTimings = new HashMap<>();
try {
long startNano = System.nanoTime();
final AccountInvoices accountInvoices = invoiceOptimizer.getInvoices(context);
invoiceTimings.put(InvoiceTiming.FETCH_INVOICES, System.nanoTime() - startNano);
// Make sure to first set the BCD if needed then get the account object (to have the BCD set)
startNano = System.nanoTime();
final BillingEventSet billingEvents = billingApi.getBillingEventsForAccountAndUpdateAccountBCD(accountId, dryRunArguments, accountInvoices.getBillingEventCutoffDate(), context);
invoiceTimings.put(InvoiceTiming.BILLING_EVENTS, System.nanoTime() - startNano);
if (!isApiCall && billingEvents.isAccountAutoInvoiceOff()) {
return null;
}
final Invoice invoice;
if (!isDryRun) {
final InvoiceWithFutureNotifications invoiceWithFutureNotifications = processAccountWithLockAndInputTargetDate(accountId, inputTargetDate, billingEvents, accountInvoices, dryRunInfo, isRescheduled, Lists.newLinkedList(), invoiceTimings, context);
invoice = invoiceWithFutureNotifications != null ? invoiceWithFutureNotifications.getInvoice() : null;
if (parkedAccount) {
try {
log.info("Illegal invoicing state fixed for accountId='{}', unparking account", accountId);
parkedAccountsManager.unparkAccount(accountId, context);
} catch (final TagApiException ignored) {
log.warn("Unable to unpark account", ignored);
}
}
} else /* Dry run use cases */
{
final NotificationQueue notificationQueue = notificationQueueService.getNotificationQueue(KILLBILL_SERVICES.INVOICE_SERVICE.getServiceName(), DefaultNextBillingDateNotifier.NEXT_BILLING_DATE_NOTIFIER_QUEUE);
final Iterable<NotificationEventWithMetadata<NextBillingDateNotificationKey>> futureNotificationsIterable = notificationQueue.getFutureNotificationForSearchKeys(context.getAccountRecordId(), context.getTenantRecordId());
// Copy the results as retrieving the iterator will issue a query each time. This also makes sure the underlying JDBC connection is closed.
final List<NotificationEventWithMetadata<NextBillingDateNotificationKey>> futureNotifications = ImmutableList.<NotificationEventWithMetadata<NextBillingDateNotificationKey>>copyOf(futureNotificationsIterable);
final Map<UUID, DateTime> nextScheduledSubscriptionsEventMap = getNextTransitionsForSubscriptions(billingEvents);
// List of all existing invoice notifications
final Set<LocalDate> allCandidateTargetDates = getUpcomingInvoiceCandidateDates(futureNotifications, nextScheduledSubscriptionsEventMap, ImmutableList.<UUID>of(), context);
if (dryRunArguments.getDryRunType() == DryRunType.UPCOMING_INVOICE) {
final Iterable<UUID> filteredSubscriptionIdsForDryRun = getFilteredSubscriptionIdsFor_UPCOMING_INVOICE_DryRun(dryRunArguments, billingEvents);
// List of existing invoice notifications associated to the filter set of subscriptionIds
final Set<LocalDate> filteredCandidateTargetDates = Iterables.isEmpty(filteredSubscriptionIdsForDryRun) ? allCandidateTargetDates : getUpcomingInvoiceCandidateDates(futureNotifications, nextScheduledSubscriptionsEventMap, filteredSubscriptionIdsForDryRun, context);
if (Iterables.isEmpty(filteredSubscriptionIdsForDryRun)) {
invoice = processDryRun_UPCOMING_INVOICE_Invoice(accountId, allCandidateTargetDates, billingEvents, accountInvoices, dryRunInfo, invoiceTimings, context);
} else {
invoice = processDryRun_UPCOMING_INVOICE_FILTERING_Invoice(accountId, filteredCandidateTargetDates, allCandidateTargetDates, billingEvents, accountInvoices, dryRunInfo, invoiceTimings, context);
}
} else /* DryRunType.TARGET_DATE, SUBSCRIPTION_ACTION */
{
invoice = processDryRun_TARGET_DATE_Invoice(accountId, inputTargetDate, allCandidateTargetDates, billingEvents, accountInvoices, dryRunInfo, invoiceTimings, context);
}
}
printInvoiceTiming(invoiceTimings);
return invoice;
} catch (final CatalogApiException e) {
log.warn("Failed to retrieve BillingEvents for accountId='{}', dryRunArguments='{}'", accountId, dryRunArguments, e);
return null;
} catch (final AccountApiException e) {
log.warn("Failed to retrieve BillingEvents for accountId='{}', dryRunArguments='{}'", accountId, dryRunArguments, e);
return null;
} catch (final SubscriptionBaseApiException e) {
log.warn("Failed to retrieve BillingEvents for accountId='{}', dryRunArguments='{}'", accountId, dryRunArguments, e);
return null;
} catch (final InvoiceApiException e) {
if (e.getCode() == ErrorCode.INVOICE_PLUGIN_API_ABORTED.getCode()) {
return null;
}
if (e.getCode() == ErrorCode.UNEXPECTED_ERROR.getCode() && !isDryRun) {
log.warn("Illegal invoicing state detected for accountId='{}', dryRunArguments='{}', parking account", accountId, dryRunArguments, e);
parkAccount(accountId, context);
}
throw e;
} catch (final NoSuchNotificationQueue e) {
throw new InvoiceApiException(ErrorCode.UNEXPECTED_ERROR, "Failed to retrieve future notifications from notificationQ");
}
}
use of org.killbill.billing.util.api.TagApiException in project killbill by killbill.
the class InvoiceDispatcher method processAccount.
public Invoice processAccount(final boolean isApiCall, final UUID accountId, @Nullable final LocalDate targetDate, @Nullable final DryRunArguments dryRunArguments, final boolean isRescheduled, final InternalCallContext context) throws InvoiceApiException {
boolean parkedAccount = false;
try {
parkedAccount = parkedAccountsManager.isParked(context);
if (parkedAccount && !isApiCall) {
log.warn("Ignoring invoice generation process for accountId='{}', targetDate='{}', account is parked", accountId.toString(), targetDate);
return null;
}
} catch (final TagApiException e) {
log.warn("Unable to determine parking state for accountId='{}'", accountId);
}
if (!isApiCall && !locker.isFree(LockerType.ACCNT_INV_PAY.toString(), accountId.toString())) {
if (invoiceOptimizer.rescheduleProcessAccount(accountId, context)) {
return null;
}
}
GlobalLock lock = null;
try {
// Grab lock unless we do a dry-run
final boolean isDryRun = dryRunArguments != null;
lock = !isDryRun ? locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), accountId.toString(), invoiceConfig.getMaxGlobalLockRetries()) : null;
return processAccountInternal(isApiCall, parkedAccount, accountId, targetDate, dryRunArguments, isRescheduled, context);
} catch (final LockFailedException e) {
if (isApiCall) {
throw new InvoiceApiException(e, ErrorCode.UNEXPECTED_ERROR, "Failed to generate invoice: failed to acquire lock");
}
if (!invoiceOptimizer.rescheduleProcessAccount(accountId, context)) {
log.warn("Failed to process invoice for accountId='{}', targetDate='{}'", accountId.toString(), targetDate, e);
}
} finally {
if (lock != null) {
lock.release();
}
}
return null;
}
Aggregations