use of org.killbill.billing.util.tag.Tag in project killbill by killbill.
the class TestAccountDao method testTags.
@Test(groups = "slow", description = "Test Account DAO: tags")
public void testTags() throws TagApiException, TagDefinitionApiException {
final AccountModelDao account = createTestAccount();
final TagDefinitionModelDao tagDefinition = tagDefinitionDao.create(UUID.randomUUID().toString().substring(0, 4), UUID.randomUUID().toString(), internalCallContext);
final Tag tag = new DescriptiveTag(tagDefinition.getId(), ObjectType.ACCOUNT, account.getId(), internalCallContext.getCreatedDate());
tagDao.create(new TagModelDao(tag), internalCallContext);
final List<TagModelDao> tags = tagDao.getTagsForObject(account.getId(), ObjectType.ACCOUNT, false, internalCallContext);
Assert.assertEquals(tags.size(), 1);
Assert.assertEquals(tags.get(0).getTagDefinitionId(), tagDefinition.getId());
Assert.assertEquals(tags.get(0).getObjectId(), account.getId());
Assert.assertEquals(tags.get(0).getObjectType(), ObjectType.ACCOUNT);
}
use of org.killbill.billing.util.tag.Tag in project killbill by killbill.
the class AccountResource method getAllTags.
@TimedResource
@GET
@Path("/{accountId:" + UUID_PATTERN + "}/" + ALL_TAGS)
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Retrieve account tags", response = TagJson.class, responseContainer = "List")
@ApiResponses(value = { @ApiResponse(code = 400, message = "Invalid account id supplied"), @ApiResponse(code = 404, message = "Account not found") })
public Response getAllTags(@PathParam(ID_PARAM_NAME) final String accountIdString, @QueryParam(QUERY_OBJECT_TYPE) final ObjectType objectType, @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode, @QueryParam(QUERY_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted, @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException {
final UUID accountId = UUID.fromString(accountIdString);
final TenantContext tenantContext = context.createContext(request);
final List<Tag> tags = objectType != null ? tagUserApi.getTagsForAccountType(accountId, objectType, includedDeleted, tenantContext) : tagUserApi.getTagsForAccount(accountId, includedDeleted, tenantContext);
return createTagResponse(accountId, tags, auditMode, tenantContext);
}
use of org.killbill.billing.util.tag.Tag in project killbill by killbill.
the class AdminResource method triggerInvoiceGenerationForParkedAccounts.
@POST
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@Path("/invoices")
@ApiOperation(value = "Trigger an invoice generation for all parked accounts")
@ApiResponses(value = {})
public Response triggerInvoiceGenerationForParkedAccounts(@QueryParam(QUERY_SEARCH_OFFSET) @DefaultValue("0") final Long offset, @QueryParam(QUERY_SEARCH_LIMIT) @DefaultValue("100") final Long limit, @HeaderParam(HDR_CREATED_BY) final String createdBy, @HeaderParam(HDR_REASON) final String reason, @HeaderParam(HDR_COMMENT) final String comment, @javax.ws.rs.core.Context final HttpServletRequest request) {
final CallContext callContext = context.createContext(createdBy, reason, comment, request);
// TODO Consider adding a real invoice API post 0.18.x
final Pagination<Tag> tags = tagUserApi.searchTags(SystemTags.PARK_TAG_DEFINITION_NAME, offset, limit, callContext);
final Iterator<Tag> iterator = tags.iterator();
final StreamingOutput json = new StreamingOutput() {
@Override
public void write(final OutputStream output) throws IOException, WebApplicationException {
try {
final JsonGenerator generator = mapper.getFactory().createGenerator(output);
generator.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false);
generator.writeStartObject();
while (iterator.hasNext()) {
final Tag tag = iterator.next();
final UUID accountId = tag.getObjectId();
try {
invoiceUserApi.triggerInvoiceGeneration(accountId, clock.getUTCToday(), null, callContext);
generator.writeStringField(accountId.toString(), OK);
} catch (final InvoiceApiException e) {
if (e.getCode() != ErrorCode.INVOICE_NOTHING_TO_DO.getCode()) {
log.warn("Unable to trigger invoice generation for accountId='{}'", accountId);
}
generator.writeStringField(accountId.toString(), ErrorCode.fromCode(e.getCode()).toString());
}
}
generator.writeEndObject();
generator.close();
} finally {
// In case the client goes away (IOException), make sure to close the underlying DB connection
while (iterator.hasNext()) {
iterator.next();
}
}
}
};
final URI nextPageUri = uriBuilder.nextPage(AdminResource.class, "triggerInvoiceGenerationForParkedAccounts", tags.getNextOffset(), limit, ImmutableMap.<String, String>of());
return Response.status(Status.OK).entity(json).header(HDR_PAGINATION_CURRENT_OFFSET, tags.getCurrentOffset()).header(HDR_PAGINATION_NEXT_OFFSET, tags.getNextOffset()).header(HDR_PAGINATION_TOTAL_NB_RECORDS, tags.getTotalNbRecords()).header(HDR_PAGINATION_MAX_NB_RECORDS, tags.getMaxNbRecords()).header(HDR_PAGINATION_NEXT_PAGE_URI, nextPageUri).build();
}
use of org.killbill.billing.util.tag.Tag in project killbill by killbill.
the class DefaultInvoiceDao method deleteCBA.
@Override
public void deleteCBA(final UUID accountId, final UUID invoiceId, final UUID invoiceItemId, final InternalCallContext context) throws InvoiceApiException {
final List<Tag> invoicesTags = getInvoicesTags(context);
transactionalSqlDao.execute(InvoiceApiException.class, new EntitySqlDaoTransactionWrapper<Void>() {
@Override
public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
final InvoiceSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
// Retrieve the invoice and make sure it belongs to the right account
final InvoiceModelDao invoice = transactional.getById(invoiceId.toString(), context);
if (invoice == null || !invoice.getAccountId().equals(accountId)) {
throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceId);
}
// Retrieve the invoice item and make sure it belongs to the right invoice
final InvoiceItemSqlDao invoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
final InvoiceItemModelDao cbaItem = invoiceItemSqlDao.getById(invoiceItemId.toString(), context);
if (cbaItem == null || !cbaItem.getInvoiceId().equals(invoice.getId())) {
throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_NOT_FOUND, invoiceItemId);
}
// First, adjust the same invoice with the CBA amount to "delete"
final InvoiceItemModelDao cbaAdjItem = new InvoiceItemModelDao(context.getCreatedDate(), InvoiceItemType.CBA_ADJ, invoice.getId(), invoice.getAccountId(), null, null, null, null, null, null, context.getCreatedDate().toLocalDate(), null, cbaItem.getAmount().negate(), null, cbaItem.getCurrency(), cbaItem.getId());
createInvoiceItemFromTransaction(invoiceItemSqlDao, cbaAdjItem, context);
// Verify the final invoice balance is not negative
invoiceDaoHelper.populateChildren(invoice, invoicesTags, entitySqlDaoWrapperFactory, context);
if (InvoiceModelDaoHelper.getBalance(invoice).compareTo(BigDecimal.ZERO) < 0) {
throw new InvoiceApiException(ErrorCode.INVOICE_WOULD_BE_NEGATIVE);
}
// If there is more account credit than CBA we adjusted, we're done.
// Otherwise, we need to find further invoices on which this credit was consumed
final BigDecimal accountCBA = cbaDao.getAccountCBAFromTransaction(entitySqlDaoWrapperFactory, context);
if (accountCBA.compareTo(BigDecimal.ZERO) < 0) {
if (accountCBA.compareTo(cbaItem.getAmount().negate()) < 0) {
throw new IllegalStateException("The account balance can't be lower than the amount adjusted");
}
final List<InvoiceModelDao> invoicesFollowing = getAllNonMigratedInvoicesByAccountAfterDate(transactional, invoice.getInvoiceDate(), context);
invoiceDaoHelper.populateChildren(invoicesFollowing, invoicesTags, entitySqlDaoWrapperFactory, context);
// The remaining amount to adjust (i.e. the amount of credits used on following invoices)
// is the current account CBA balance (minus the sign)
BigDecimal positiveRemainderToAdjust = accountCBA.negate();
for (final InvoiceModelDao invoiceFollowing : invoicesFollowing) {
if (invoiceFollowing.getId().equals(invoice.getId())) {
continue;
}
// Add a single adjustment per invoice
BigDecimal positiveCBAAdjItemAmount = BigDecimal.ZERO;
for (final InvoiceItemModelDao cbaUsed : invoiceFollowing.getInvoiceItems()) {
// Ignore non CBA items or credits (CBA >= 0)
if (!InvoiceItemType.CBA_ADJ.equals(cbaUsed.getType()) || cbaUsed.getAmount().compareTo(BigDecimal.ZERO) >= 0) {
continue;
}
final BigDecimal positiveCBAUsedAmount = cbaUsed.getAmount().negate();
final BigDecimal positiveNextCBAAdjItemAmount;
if (positiveCBAUsedAmount.compareTo(positiveRemainderToAdjust) < 0) {
positiveNextCBAAdjItemAmount = positiveCBAUsedAmount;
positiveRemainderToAdjust = positiveRemainderToAdjust.subtract(positiveNextCBAAdjItemAmount);
} else {
positiveNextCBAAdjItemAmount = positiveRemainderToAdjust;
positiveRemainderToAdjust = BigDecimal.ZERO;
}
positiveCBAAdjItemAmount = positiveCBAAdjItemAmount.add(positiveNextCBAAdjItemAmount);
if (positiveRemainderToAdjust.compareTo(BigDecimal.ZERO) == 0) {
break;
}
}
// Add the adjustment on that invoice
final InvoiceItemModelDao nextCBAAdjItem = new InvoiceItemModelDao(context.getCreatedDate(), InvoiceItemType.CBA_ADJ, invoiceFollowing.getId(), invoice.getAccountId(), null, null, null, null, null, null, context.getCreatedDate().toLocalDate(), null, positiveCBAAdjItemAmount, null, cbaItem.getCurrency(), cbaItem.getId());
createInvoiceItemFromTransaction(invoiceItemSqlDao, nextCBAAdjItem, context);
if (positiveRemainderToAdjust.compareTo(BigDecimal.ZERO) == 0) {
break;
}
}
}
return null;
}
});
}
use of org.killbill.billing.util.tag.Tag 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;
}
Aggregations