use of org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory in project killbill by killbill.
the class DefaultSubscriptionDao method changePlan.
@Override
public void changePlan(final DefaultSubscriptionBase subscription, final List<SubscriptionBaseEvent> originalInputChangeEvents, final List<DefaultSubscriptionBase> subscriptionsToBeCancelled, final List<SubscriptionBaseEvent> cancelEvents, final SubscriptionCatalog catalog, final InternalCallContext context) {
// First event is expected to be the subscription CHANGE event
final SubscriptionBaseEvent inputChangeEvent = originalInputChangeEvents.get(0);
Preconditions.checkState(inputChangeEvent.getType() == EventType.API_USER && ((ApiEvent) inputChangeEvent).getApiEventType() == ApiEventType.CHANGE);
Preconditions.checkState(inputChangeEvent.getSubscriptionId().equals(subscription.getId()));
transactionalSqlDao.execute(false, new EntitySqlDaoTransactionWrapper<Void>() {
@Override
public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
final SubscriptionEventSqlDao transactional = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class);
final List<SubscriptionEventModelDao> activeSubscriptionEvents = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class).getActiveEventsForSubscription(subscription.getId().toString(), context);
// First event is CREATE/TRANSFER event
final SubscriptionEventModelDao firstSubscriptionEvent = activeSubscriptionEvents.get(0);
final Iterable<SubscriptionEventModelDao> activePresentOrFutureSubscriptionEvents = Iterables.filter(activeSubscriptionEvents, new Predicate<SubscriptionEventModelDao>() {
@Override
public boolean apply(SubscriptionEventModelDao input) {
return input.getEffectiveDate().compareTo(inputChangeEvent.getEffectiveDate()) >= 0;
}
});
// We do a little magic here in case the CHANGE coincides exactly with the CREATE event to invalidate original CREATE event and
// change the input CHANGE event into a CREATE event.
final boolean isChangePlanOnStartDate = firstSubscriptionEvent.getEffectiveDate().compareTo(inputChangeEvent.getEffectiveDate()) == 0;
final List<SubscriptionBaseEvent> inputChangeEvents;
if (isChangePlanOnStartDate) {
// Rebuild input event list with first the CREATE event and all original input events except for inputChangeEvent
inputChangeEvents = new ArrayList<SubscriptionBaseEvent>();
final SubscriptionBaseEvent newCreateEvent = new ApiEventBuilder((ApiEventChange) inputChangeEvent).setApiEventType(firstSubscriptionEvent.getUserType()).build();
originalInputChangeEvents.remove(0);
inputChangeEvents.add(newCreateEvent);
inputChangeEvents.addAll(originalInputChangeEvents);
// Deactivate original CREATE event
unactivateEventFromTransaction(firstSubscriptionEvent, entitySqlDaoWrapperFactory, context);
} else {
inputChangeEvents = originalInputChangeEvents;
}
cancelFutureEventsFromTransaction(activePresentOrFutureSubscriptionEvents, entitySqlDaoWrapperFactory, false, context);
for (final SubscriptionBaseEvent cur : inputChangeEvents) {
createAndRefresh(transactional, new SubscriptionEventModelDao(cur), context);
final boolean isBusEvent = cur.getEffectiveDate().compareTo(context.getCreatedDate()) <= 0 && (cur.getType() == EventType.API_USER || cur.getType() == EventType.BCD_UPDATE);
recordBusOrFutureNotificationFromTransaction(subscription, cur, entitySqlDaoWrapperFactory, isBusEvent, 0, catalog, context);
}
// Notify the Bus of the latest requested change
final SubscriptionBaseEvent finalEvent = inputChangeEvents.get(inputChangeEvents.size() - 1);
notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, finalEvent, SubscriptionBaseTransitionType.CHANGE, 0, context);
// Cancel associated add-ons
cancelSubscriptionsFromTransaction(entitySqlDaoWrapperFactory, subscriptionsToBeCancelled, cancelEvents, catalog, context);
return null;
}
});
}
use of org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory in project killbill by killbill.
the class DefaultInvoiceDao method createInvoices.
private List<InvoiceItemModelDao> createInvoices(final Iterable<InvoiceModelDao> invoices, final FutureAccountNotifications callbackDateTimePerSubscriptions, final InternalCallContext context) {
final Collection<UUID> createdInvoiceIds = new HashSet<UUID>();
final Collection<UUID> adjustedInvoiceIds = new HashSet<UUID>();
final Collection<UUID> committedInvoiceIds = new HashSet<UUID>();
final Collection<UUID> uniqueInvoiceIds = new HashSet<UUID>();
for (final InvoiceModelDao invoiceModelDao : invoices) {
for (final InvoiceItemModelDao invoiceItemModelDao : invoiceModelDao.getInvoiceItems()) {
uniqueInvoiceIds.add(invoiceItemModelDao.getInvoiceId());
}
}
if (Iterables.<InvoiceModelDao>isEmpty(invoices)) {
return ImmutableList.<InvoiceItemModelDao>of();
}
final UUID accountId = invoices.iterator().next().getAccountId();
final List<Tag> invoicesTags = getInvoicesTags(context);
final Map<UUID, InvoiceModelDao> invoiceByInvoiceId = new HashMap<UUID, InvoiceModelDao>();
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceItemModelDao>>() {
@Override
public List<InvoiceItemModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
final InvoiceSqlDao invoiceSqlDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
final InvoiceItemSqlDao transInvoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
final List<InvoiceItemModelDao> createdInvoiceItems = new LinkedList<InvoiceItemModelDao>();
for (final InvoiceModelDao invoiceModelDao : invoices) {
invoiceByInvoiceId.put(invoiceModelDao.getId(), invoiceModelDao);
final boolean isRealInvoice = uniqueInvoiceIds.remove(invoiceModelDao.getId());
// Create the invoice if needed
if (invoiceSqlDao.getById(invoiceModelDao.getId().toString(), context) == null) {
// a shell invoice and we only need to insert the invoiceItems -- for the already existing invoices
if (isRealInvoice) {
invoiceSqlDao.create(invoiceModelDao, context);
createdInvoiceIds.add(invoiceModelDao.getId());
}
}
// Create the invoice items if needed (note: they may not necessarily belong to that invoice)
for (final InvoiceItemModelDao invoiceItemModelDao : invoiceModelDao.getInvoiceItems()) {
if (transInvoiceItemSqlDao.getById(invoiceItemModelDao.getId().toString(), context) == null) {
createInvoiceItemFromTransaction(transInvoiceItemSqlDao, invoiceItemModelDao, context);
createdInvoiceItems.add(transInvoiceItemSqlDao.getById(invoiceItemModelDao.getId().toString(), context));
adjustedInvoiceIds.add(invoiceItemModelDao.getInvoiceId());
}
}
final boolean wasInvoiceCreated = createdInvoiceIds.contains(invoiceModelDao.getId());
if (InvoiceStatus.COMMITTED.equals(invoiceModelDao.getStatus())) {
committedInvoiceIds.add(invoiceModelDao.getId());
notifyOfFutureBillingEvents(entitySqlDaoWrapperFactory, invoiceModelDao.getAccountId(), callbackDateTimePerSubscriptions, context);
if (wasInvoiceCreated) {
notifyBusOfInvoiceCreation(entitySqlDaoWrapperFactory, invoiceModelDao, context);
}
} else if (wasInvoiceCreated && invoiceModelDao.isParentInvoice()) {
// Commit queue
notifyOfParentInvoiceCreation(entitySqlDaoWrapperFactory, invoiceModelDao, context);
}
}
for (final UUID adjustedInvoiceId : adjustedInvoiceIds) {
final boolean newInvoice = createdInvoiceIds.contains(adjustedInvoiceId);
if (newInvoice) {
// New invoice, so no associated payment yet: no need to refresh the invoice state
cbaDao.doCBAComplexityFromTransaction(invoiceByInvoiceId.get(adjustedInvoiceId), invoicesTags, entitySqlDaoWrapperFactory, context);
} else {
// Existing invoice (e.g. we're processing an adjustment): refresh the invoice state to get the correct balance
// Should we maybe enforce callers (e.g. InvoiceApiHelper) to properly populate these invoices?
cbaDao.doCBAComplexityFromTransaction(adjustedInvoiceId, invoicesTags, entitySqlDaoWrapperFactory, context);
}
if (committedInvoiceIds.contains(adjustedInvoiceId) && !newInvoice) {
// Notify the bus since the balance of the invoice changed (only if the invoice is COMMITTED)
notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, adjustedInvoiceId, accountId, context.getUserToken(), context);
}
}
return createdInvoiceItems;
}
});
}
use of org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory in project killbill by killbill.
the class DefaultSubscriptionDao method getSubscriptionsFromAccountId.
private Map<UUID, List<SubscriptionBase>> getSubscriptionsFromAccountId(final InternalTenantContext context) {
final List<SubscriptionBase> allSubscriptions = transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<SubscriptionBase>>() {
@Override
public List<SubscriptionBase> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
final List<SubscriptionBundleModelDao> bundleModels = entitySqlDaoWrapperFactory.become(BundleSqlDao.class).getByAccountRecordId(context);
final List<SubscriptionModelDao> subscriptionModels = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class).getByAccountRecordId(context);
return new ArrayList<SubscriptionBase>(Collections2.transform(subscriptionModels, new Function<SubscriptionModelDao, SubscriptionBase>() {
@Override
public SubscriptionBase apply(final SubscriptionModelDao input) {
final SubscriptionBundleModelDao bundleModel = Iterables.find(bundleModels, new Predicate<SubscriptionBundleModelDao>() {
@Override
public boolean apply(final SubscriptionBundleModelDao bundleInput) {
return bundleInput.getId().equals(input.getBundleId());
}
});
return SubscriptionModelDao.toSubscription(input, bundleModel.getExternalKey());
}
}));
}
});
final Map<UUID, List<SubscriptionBase>> result = new HashMap<UUID, List<SubscriptionBase>>();
for (final SubscriptionBase subscriptionBase : allSubscriptions) {
if (result.get(subscriptionBase.getBundleId()) == null) {
result.put(subscriptionBase.getBundleId(), new LinkedList<SubscriptionBase>());
}
result.get(subscriptionBase.getBundleId()).add(subscriptionBase);
}
return result;
}
use of org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory in project killbill by killbill.
the class DefaultInvoiceDao method transferChildCreditToParent.
@Override
public void transferChildCreditToParent(final Account childAccount, final InternalCallContext childAccountContext) throws InvoiceApiException {
// Need to create an internalCallContext for parent account because it's needed to save the correct accountRecordId in Invoice tables.
// Then it's used to load invoices by account.
final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(childAccount.getParentAccountId(), childAccountContext);
final InternalCallContext parentAccountContext = internalCallContextFactory.createInternalCallContext(internalTenantContext.getAccountRecordId(), childAccountContext);
final List<Tag> parentInvoicesTags = getInvoicesTags(parentAccountContext);
final List<Tag> childInvoicesTags = getInvoicesTags(childAccountContext);
transactionalSqlDao.execute(false, new EntitySqlDaoTransactionWrapper<Void>() {
@Override
public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
final InvoiceSqlDao invoiceSqlDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
final InvoiceItemSqlDao transInvoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
// create child and parent invoices
final DateTime childCreatedDate = childAccountContext.getCreatedDate();
final BigDecimal accountCBA = getAccountCBA(childAccount.getId(), childAccountContext);
// create external charge to child account
final LocalDate childInvoiceDate = childAccountContext.toLocalDate(childAccountContext.getCreatedDate());
final Invoice invoiceForExternalCharge = new DefaultInvoice(childAccount.getId(), childInvoiceDate, childCreatedDate.toLocalDate(), childAccount.getCurrency(), InvoiceStatus.COMMITTED);
final String chargeDescription = "Charge to move credit from child to parent account";
final InvoiceItem externalChargeItem = new ExternalChargeInvoiceItem(UUIDs.randomUUID(), childCreatedDate, invoiceForExternalCharge.getId(), childAccount.getId(), null, chargeDescription, childCreatedDate.toLocalDate(), childCreatedDate.toLocalDate(), accountCBA, childAccount.getCurrency(), null);
invoiceForExternalCharge.addInvoiceItem(externalChargeItem);
// create credit to parent account
final LocalDate parentInvoiceDate = parentAccountContext.toLocalDate(parentAccountContext.getCreatedDate());
final Invoice invoiceForCredit = new DefaultInvoice(childAccount.getParentAccountId(), parentInvoiceDate, childCreatedDate.toLocalDate(), childAccount.getCurrency(), InvoiceStatus.COMMITTED);
final String creditDescription = "Credit migrated from child account " + childAccount.getId();
final InvoiceItem creditItem = new CreditAdjInvoiceItem(UUIDs.randomUUID(), childCreatedDate, invoiceForCredit.getId(), childAccount.getParentAccountId(), childCreatedDate.toLocalDate(), creditDescription, // Note! The amount is negated here!
accountCBA.negate(), childAccount.getCurrency(), null);
invoiceForCredit.addInvoiceItem(creditItem);
// save invoices and invoice items
final InvoiceModelDao childInvoice = new InvoiceModelDao(invoiceForExternalCharge);
createAndRefresh(invoiceSqlDao, childInvoice, childAccountContext);
final InvoiceItemModelDao childExternalChargeItem = new InvoiceItemModelDao(externalChargeItem);
createInvoiceItemFromTransaction(transInvoiceItemSqlDao, childExternalChargeItem, childAccountContext);
// Keep invoice up-to-date for CBA below
childInvoice.addInvoiceItem(childExternalChargeItem);
final InvoiceModelDao parentInvoice = new InvoiceModelDao(invoiceForCredit);
createAndRefresh(invoiceSqlDao, parentInvoice, parentAccountContext);
final InvoiceItemModelDao parentCreditItem = new InvoiceItemModelDao(creditItem);
createInvoiceItemFromTransaction(transInvoiceItemSqlDao, parentCreditItem, parentAccountContext);
// Keep invoice up-to-date for CBA below
parentInvoice.addInvoiceItem(parentCreditItem);
// Create Mapping relation
final InvoiceParentChildrenSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceParentChildrenSqlDao.class);
final InvoiceParentChildModelDao invoiceRelation = new InvoiceParentChildModelDao(parentInvoice.getId(), childInvoice.getId(), childInvoice.getAccountId());
createAndRefresh(transactional, invoiceRelation, parentAccountContext);
// Add child CBA complexity and notify bus on child invoice creation
final CBALogicWrapper childCbaWrapper = new CBALogicWrapper(childAccount.getId(), childInvoicesTags, childAccountContext, entitySqlDaoWrapperFactory);
childCbaWrapper.runCBALogicWithNotificationEvents(ImmutableSet.of(), ImmutableSet.of(childInvoice.getId()), ImmutableList.of(childInvoice));
notifyBusOfInvoiceCreation(entitySqlDaoWrapperFactory, childInvoice, childAccountContext);
// Add parent CBA complexity and notify bus on child invoice creation
final CBALogicWrapper cbaWrapper = new CBALogicWrapper(childAccount.getParentAccountId(), parentInvoicesTags, parentAccountContext, entitySqlDaoWrapperFactory);
cbaWrapper.runCBALogicWithNotificationEvents(ImmutableSet.of(), ImmutableSet.of(parentInvoice.getId()), ImmutableList.of(parentInvoice));
notifyBusOfInvoiceCreation(entitySqlDaoWrapperFactory, parentInvoice, parentAccountContext);
return null;
}
});
}
use of org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory in project killbill by killbill.
the class DefaultInvoiceDao method changeInvoiceStatus.
@Override
public void changeInvoiceStatus(final UUID invoiceId, final InvoiceStatus newStatus, final InternalCallContext context) throws InvoiceApiException {
final List<Tag> invoicesTags = getInvoicesTags(context);
transactionalSqlDao.execute(false, 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) {
throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceId);
}
if (invoice.getStatus().equals(newStatus) || invoice.getStatus().equals(InvoiceStatus.VOID)) {
throw new InvoiceApiException(ErrorCode.INVOICE_INVALID_STATUS, newStatus, invoiceId, invoice.getStatus());
}
transactional.updateStatusAndTargetDate(invoiceId.toString(), newStatus.toString(), invoice.getTargetDate(), context);
// Run through all invoices
// Current invoice could be a credit item that needs to be rebalanced
cbaDao.doCBAComplexityFromTransaction(invoicesTags, entitySqlDaoWrapperFactory, context);
// Invoice creation event sent on COMMITTED
if (InvoiceStatus.COMMITTED.equals(newStatus)) {
notifyBusOfInvoiceCreation(entitySqlDaoWrapperFactory, invoice, context);
} else if (InvoiceStatus.VOID.equals(newStatus)) {
// https://github.com/killbill/killbill/issues/1448
notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, invoiceId, invoice.getAccountId(), context.getUserToken(), context);
// Deactivate any usage trackingIds if necessary
final InvoiceTrackingSqlDao trackingSqlDao = entitySqlDaoWrapperFactory.become(InvoiceTrackingSqlDao.class);
final List<InvoiceTrackingModelDao> invoiceTrackingModelDaos = trackingSqlDao.getTrackingsForInvoices(ImmutableList.of(invoiceId.toString()), context);
if (!invoiceTrackingModelDaos.isEmpty()) {
final Collection<String> invoiceTrackingIdsToDeactivate = Collections2.<InvoiceTrackingModelDao, String>transform(invoiceTrackingModelDaos, new Function<InvoiceTrackingModelDao, String>() {
@Override
public String apply(final InvoiceTrackingModelDao input) {
return input.getId().toString();
}
});
trackingSqlDao.deactivateByIds(invoiceTrackingIdsToDeactivate, context);
}
}
return null;
}
});
}
Aggregations