use of org.killbill.billing.invoice.api.InvoiceItem in project killbill by killbill.
the class SubscriptionItemTree method checkItemsListState.
// Verify there is no double billing, and no double repair (credits)
private void checkItemsListState(final List<InvoiceItem> orderedList) {
LocalDate prevRecurringEndDate = null;
LocalDate prevRepairEndDate = null;
for (InvoiceItem cur : orderedList) {
switch(cur.getInvoiceItemType()) {
case FIXED:
break;
case RECURRING:
if (prevRecurringEndDate != null) {
Preconditions.checkState(prevRecurringEndDate.compareTo(cur.getStartDate()) <= 0);
}
prevRecurringEndDate = cur.getEndDate();
break;
case REPAIR_ADJ:
if (prevRepairEndDate != null) {
Preconditions.checkState(prevRepairEndDate.compareTo(cur.getStartDate()) <= 0);
}
prevRepairEndDate = cur.getEndDate();
break;
default:
Preconditions.checkState(false, "Unexpected item type " + cur.getInvoiceItemType());
}
}
}
use of org.killbill.billing.invoice.api.InvoiceItem in project killbill by killbill.
the class SubscriptionItemTree method build.
/**
* Build the tree and process adjustments
*/
public void build() {
Preconditions.checkState(!isBuilt);
for (final InvoiceItem item : pendingItemAdj) {
final Item fullyAdjustedItem = root.addAdjustment(item, targetInvoiceId);
if (fullyAdjustedItem != null) {
existingFullyAdjustedItems.add(fullyAdjustedItem);
}
}
pendingItemAdj.clear();
root.buildForExistingItems(items, targetInvoiceId);
isBuilt = true;
}
use of org.killbill.billing.invoice.api.InvoiceItem in project killbill by killbill.
the class RawUsageOptimizer method getOptimizedRawUsageStartDate.
@VisibleForTesting
LocalDate getOptimizedRawUsageStartDate(final LocalDate firstEventStartDate, final LocalDate targetDate, final Iterable<InvoiceItem> existingUsageItems, final Map<String, Usage> knownUsage, final InternalCallContext internalCallContext) {
if (!existingUsageItems.iterator().hasNext()) {
return firstEventStartDate;
}
// Extract all usage billing period known in that catalog
final Set<BillingPeriod> knownUsageBillingPeriod = ImmutableSet.copyOf(Iterables.transform(knownUsage.values(), new Function<Usage, BillingPeriod>() {
@Nullable
@Override
public BillingPeriod apply(final Usage input) {
return input.getBillingPeriod();
}
}));
// Make sure all usage items are sorted by endDate
final List<InvoiceItem> sortedUsageItems = USAGE_ITEM_ORDERING.sortedCopy(existingUsageItems);
// Compute an array with one date per BillingPeriod:
// If BillingPeriod is never defined in the catalog (no need to look for items), we initialize its value
// such that it cannot be chosen
//
// Exclude the NO_BILLING_PERIOD
final LocalDate[] perBillingPeriodMostRecentConsumableInArrearItemEndDate = new LocalDate[BillingPeriod.values().length - 1];
int idx = 0;
for (final BillingPeriod bp : BillingPeriod.values()) {
if (bp != BillingPeriod.NO_BILLING_PERIOD) {
final LocalDate makerDateThanCannotBeChosenAsTheMinOfAllDates = InvoiceDateUtils.advanceByNPeriods(targetDate, bp, config.getMaxRawUsagePreviousPeriod(internalCallContext));
perBillingPeriodMostRecentConsumableInArrearItemEndDate[idx++] = (knownUsageBillingPeriod.contains(bp)) ? null : makerDateThanCannotBeChosenAsTheMinOfAllDates;
}
}
final ListIterator<InvoiceItem> iterator = sortedUsageItems.listIterator(sortedUsageItems.size());
while (iterator.hasPrevious()) {
final InvoiceItem previous = iterator.previous();
Preconditions.checkState(previous instanceof UsageInvoiceItem);
final UsageInvoiceItem item = (UsageInvoiceItem) previous;
final Usage usage = knownUsage.get(item.getUsageName());
if (perBillingPeriodMostRecentConsumableInArrearItemEndDate[usage.getBillingPeriod().ordinal()] == null) {
perBillingPeriodMostRecentConsumableInArrearItemEndDate[usage.getBillingPeriod().ordinal()] = item.getEndDate();
if (!containsNullEntries(perBillingPeriodMostRecentConsumableInArrearItemEndDate)) {
break;
}
}
}
// Extract the min from all the dates
LocalDate targetStartDate = null;
idx = 0;
for (final BillingPeriod bp : BillingPeriod.values()) {
if (bp != BillingPeriod.NO_BILLING_PERIOD) {
final LocalDate tmp = perBillingPeriodMostRecentConsumableInArrearItemEndDate[idx];
final LocalDate targetBillingPeriodDate = tmp != null ? InvoiceDateUtils.recedeByNPeriods(tmp, bp, config.getMaxRawUsagePreviousPeriod(internalCallContext)) : null;
if (targetStartDate == null || (targetBillingPeriodDate != null && targetBillingPeriodDate.compareTo(targetStartDate) < 0)) {
targetStartDate = targetBillingPeriodDate;
}
idx++;
}
}
final LocalDate result = targetStartDate.compareTo(firstEventStartDate) > 0 ? targetStartDate : firstEventStartDate;
return result;
}
use of org.killbill.billing.invoice.api.InvoiceItem in project killbill by killbill.
the class DefaultInvoiceUserApi method insertInvoiceItemAdjustment.
@Override
public InvoiceItem insertInvoiceItemAdjustment(final UUID accountId, final UUID invoiceId, final UUID invoiceItemId, final LocalDate effectiveDate, @Nullable final BigDecimal amount, @Nullable final Currency currency, final String description, final CallContext context) throws InvoiceApiException {
if (amount != null && amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_ADJUSTMENT_AMOUNT_SHOULD_BE_POSITIVE, amount);
}
final WithAccountLock withAccountLock = new WithAccountLock() {
@Override
public Iterable<Invoice> prepareInvoices() throws InvoiceApiException {
final Invoice invoice = getInvoiceAndCheckCurrency(invoiceId, currency, context);
final InvoiceItem adjustmentItem = invoiceApiHelper.createAdjustmentItem(invoice, invoiceItemId, amount, currency, effectiveDate, description, internalCallContextFactory.createInternalCallContext(accountId, context));
invoice.addInvoiceItem(adjustmentItem);
return ImmutableList.<Invoice>of(invoice);
}
};
final Collection<InvoiceItem> adjustmentInvoiceItems = Collections2.<InvoiceItem>filter(invoiceApiHelper.dispatchToInvoicePluginsAndInsertItems(accountId, false, withAccountLock, context), new Predicate<InvoiceItem>() {
@Override
public boolean apply(final InvoiceItem invoiceItem) {
return InvoiceItemType.ITEM_ADJ.equals(invoiceItem.getInvoiceItemType());
}
});
Preconditions.checkState(adjustmentInvoiceItems.size() == 1, "Should have created a single adjustment item: " + adjustmentInvoiceItems);
return adjustmentInvoiceItems.iterator().next();
}
use of org.killbill.billing.invoice.api.InvoiceItem in project killbill by killbill.
the class DefaultInvoiceUserApi method insertExternalCharges.
@Override
public List<InvoiceItem> insertExternalCharges(final UUID accountId, final LocalDate effectiveDate, final Iterable<InvoiceItem> charges, final boolean autoCommit, final CallContext context) throws InvoiceApiException {
for (final InvoiceItem charge : charges) {
if (charge.getAmount() == null || charge.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new InvoiceApiException(ErrorCode.EXTERNAL_CHARGE_AMOUNT_INVALID, charge.getAmount());
}
}
final WithAccountLock withAccountLock = new WithAccountLock() {
@Override
public Iterable<Invoice> prepareInvoices() throws InvoiceApiException {
final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(accountId, context);
final LocalDate invoiceDate = internalTenantContext.toLocalDate(context.getCreatedDate());
// Group all new external charges on the same invoice (per currency)
final Map<Currency, Invoice> newInvoicesForExternalCharges = new HashMap<Currency, Invoice>();
final Map<UUID, Invoice> existingInvoicesForExternalCharges = new HashMap<UUID, Invoice>();
for (final InvoiceItem charge : charges) {
final Invoice invoiceForExternalCharge;
final UUID invoiceIdForExternalCharge = charge.getInvoiceId();
// Create an invoice for that external charge if it doesn't exist
if (invoiceIdForExternalCharge == null) {
final Currency currency = charge.getCurrency();
if (newInvoicesForExternalCharges.get(currency) == null) {
final InvoiceStatus status = autoCommit ? InvoiceStatus.COMMITTED : InvoiceStatus.DRAFT;
final Invoice newInvoiceForExternalCharge = new DefaultInvoice(accountId, invoiceDate, effectiveDate, currency, status);
newInvoicesForExternalCharges.put(currency, newInvoiceForExternalCharge);
}
invoiceForExternalCharge = newInvoicesForExternalCharges.get(currency);
} else {
if (existingInvoicesForExternalCharges.get(invoiceIdForExternalCharge) == null) {
final Invoice existingInvoiceForExternalCharge = getInvoice(invoiceIdForExternalCharge, context);
existingInvoicesForExternalCharges.put(invoiceIdForExternalCharge, existingInvoiceForExternalCharge);
}
invoiceForExternalCharge = existingInvoicesForExternalCharges.get(invoiceIdForExternalCharge);
}
final InvoiceItem externalCharge = new ExternalChargeInvoiceItem(UUIDs.randomUUID(), context.getCreatedDate(), invoiceForExternalCharge.getId(), accountId, charge.getBundleId(), charge.getDescription(), effectiveDate, charge.getAmount(), charge.getCurrency());
invoiceForExternalCharge.addInvoiceItem(externalCharge);
}
return Iterables.<Invoice>concat(newInvoicesForExternalCharges.values(), existingInvoicesForExternalCharges.values());
}
};
return invoiceApiHelper.dispatchToInvoicePluginsAndInsertItems(accountId, false, withAccountLock, context);
}
Aggregations