/* * Copyright 2010-2013 Ning, Inc. * Copyright 2014-2017 Groupon, Inc * Copyright 2014-2017 The Billing Project, LLC * * The Billing Project licenses this file to you under the Apache License, version 2.0 * (the "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package org.killbill.billing.overdue.calculator; import java.math.BigDecimal; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.NoSuchElementException; import java.util.SortedSet; import java.util.TreeSet; import java.util.UUID; import org.joda.time.LocalDate; import org.killbill.billing.ObjectType; import org.killbill.billing.account.api.ImmutableAccountData; import org.killbill.billing.callcontext.InternalTenantContext; import org.killbill.billing.invoice.api.Invoice; import org.killbill.billing.invoice.api.InvoiceInternalApi; import org.killbill.billing.overdue.config.api.BillingState; import org.killbill.billing.overdue.config.api.OverdueException; import org.killbill.billing.payment.api.PaymentResponse; import org.killbill.billing.tag.TagInternalApi; import org.killbill.billing.util.tag.Tag; import org.killbill.clock.Clock; import com.google.inject.Inject; public class BillingStateCalculator { private final InvoiceInternalApi invoiceApi; private final TagInternalApi tagApi; private final Clock clock; protected class InvoiceDateComparator implements Comparator<Invoice> { @Override public int compare(final Invoice i1, final Invoice i2) { final LocalDate d1 = i1.getInvoiceDate(); final LocalDate d2 = i2.getInvoiceDate(); if (d1.compareTo(d2) == 0) { return i1.hashCode() - i2.hashCode(); // consistent (arbitrary) resolution for tied dates } return d1.compareTo(d2); } } @Inject public BillingStateCalculator(final InvoiceInternalApi invoiceApi, final Clock clock, final TagInternalApi tagApi) { this.invoiceApi = invoiceApi; this.clock = clock; this.tagApi = tagApi; } public BillingState calculateBillingState(final ImmutableAccountData account, final InternalTenantContext context) throws OverdueException { final SortedSet<Invoice> unpaidInvoices = unpaidInvoicesForAccount(account.getId(), context); final int numberOfUnpaidInvoices = unpaidInvoices.size(); final BigDecimal unpaidInvoiceBalance = sumBalance(unpaidInvoices); LocalDate dateOfEarliestUnpaidInvoice = null; UUID idOfEarliestUnpaidInvoice = null; final Invoice invoice = earliest(unpaidInvoices); if (invoice != null) { dateOfEarliestUnpaidInvoice = invoice.getInvoiceDate(); idOfEarliestUnpaidInvoice = invoice.getId(); } final PaymentResponse responseForLastFailedPayment = PaymentResponse.INSUFFICIENT_FUNDS; //TODO MDW final List<Tag> accountTags = tagApi.getTags(account.getId(), ObjectType.ACCOUNT, context); final Tag[] tags = accountTags.toArray(new Tag[accountTags.size()]); return new BillingState(account.getId(), numberOfUnpaidInvoices, unpaidInvoiceBalance, dateOfEarliestUnpaidInvoice, idOfEarliestUnpaidInvoice, responseForLastFailedPayment, tags); } // Package scope for testing Invoice earliest(final SortedSet<Invoice> unpaidInvoices) { try { return unpaidInvoices.first(); } catch (NoSuchElementException e) { return null; } } BigDecimal sumBalance(final SortedSet<Invoice> unpaidInvoices) { BigDecimal sum = BigDecimal.ZERO; for (final Invoice unpaidInvoice : unpaidInvoices) { sum = sum.add(unpaidInvoice.getBalance()); } return sum; } SortedSet<Invoice> unpaidInvoicesForAccount(final UUID accountId, final InternalTenantContext context) { final Collection<Invoice> invoices = invoiceApi.getUnpaidInvoicesByAccountId(accountId, context.toLocalDate(clock.getUTCNow()), context); final SortedSet<Invoice> sortedInvoices = new TreeSet<Invoice>(new InvoiceDateComparator()); sortedInvoices.addAll(invoices); return sortedInvoices; } }