diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
index b4254f6..1e4bf33 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
@@ -40,6 +40,7 @@ import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.tag.TagInternalApi;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
import org.killbill.billing.util.tag.ControlTagType;
import org.killbill.billing.util.tag.Tag;
@@ -57,10 +58,12 @@ public class InvoiceDaoHelper {
private static final Logger log = LoggerFactory.getLogger(InvoiceDaoHelper.class);
private final TagInternalApi tagInternalApi;
+ private final InternalCallContextFactory internalCallContextFactory;
@Inject
- public InvoiceDaoHelper(final TagInternalApi tagInternalApi) {
+ public InvoiceDaoHelper(final TagInternalApi tagInternalApi, final InternalCallContextFactory internalCallContextFactory) {
this.tagInternalApi = tagInternalApi;
+ this.internalCallContextFactory = internalCallContextFactory;
}
/**
@@ -226,12 +229,14 @@ public class InvoiceDaoHelper {
getInvoiceItemsWithinTransaction(ImmutableList.<InvoiceModelDao>of(invoice), entitySqlDaoWrapperFactory, context);
getInvoicePaymentsWithinTransaction(ImmutableList.<InvoiceModelDao>of(invoice), entitySqlDaoWrapperFactory, context);
setInvoiceWrittenOff(invoice, context);
+ getParentInvoice(ImmutableList.<InvoiceModelDao>of(invoice), entitySqlDaoWrapperFactory, context);
}
public void populateChildren(final Iterable<InvoiceModelDao> invoices, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalTenantContext context) {
getInvoiceItemsWithinTransaction(invoices, entitySqlDaoWrapperFactory, context);
getInvoicePaymentsWithinTransaction(invoices, entitySqlDaoWrapperFactory, context);
setInvoicesWrittenOff(invoices, context);
+ getParentInvoice(invoices, entitySqlDaoWrapperFactory, context);
}
public List<InvoiceModelDao> getAllInvoicesByAccountFromTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalTenantContext context) {
@@ -325,4 +330,19 @@ public class InvoiceDaoHelper {
});
}
+ private void getParentInvoice(final Iterable<InvoiceModelDao> invoices, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalTenantContext internalTenantContext) {
+
+ final InvoiceSqlDao invoiceSqlDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+ for (InvoiceModelDao invoice : invoices) {
+ if (invoice.isParentInvoice()) continue;
+ final InvoiceModelDao parentInvoice = invoiceSqlDao.getParentInvoiceByChildInvoiceId(invoice.getId().toString(), internalTenantContext);
+ if (parentInvoice != null) {
+ final Long parentAccountRecordId = internalCallContextFactory.getRecordIdFromObject(parentInvoice.getAccountId(), ObjectType.ACCOUNT, internalCallContextFactory.createTenantContext(internalTenantContext));
+ final InternalTenantContext parentContext = internalCallContextFactory.createInternalTenantContext(internalTenantContext.getTenantRecordId(), parentAccountRecordId);
+ populateChildren(parentInvoice, entitySqlDaoWrapperFactory, parentContext);
+ invoice.addParentInvoice(parentInvoice);
+ }
+ }
+ }
+
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java
index ce4da0d..589d1c7 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java
@@ -26,7 +26,6 @@ import javax.annotation.Nullable;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
-
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceStatus;
@@ -44,12 +43,13 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
private Currency currency;
private boolean migrated;
private InvoiceStatus status;
- private boolean parentInvoice;
+ private boolean isParentInvoice;
// Not in the database, for convenience only
private List<InvoiceItemModelDao> invoiceItems = new LinkedList<InvoiceItemModelDao>();
private List<InvoicePaymentModelDao> invoicePayments = new LinkedList<InvoicePaymentModelDao>();
private Currency processedCurrency;
+ private InvoiceModelDao parentInvoice;
private boolean isWrittenOff;
@@ -57,7 +57,7 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
public InvoiceModelDao(final UUID id, @Nullable final DateTime createdDate, final UUID accountId,
@Nullable final Integer invoiceNumber, final LocalDate invoiceDate, final LocalDate targetDate,
- final Currency currency, final boolean migrated, final InvoiceStatus status, final boolean parentInvoice) {
+ final Currency currency, final boolean migrated, final InvoiceStatus status, final boolean isParentInvoice) {
super(id, createdDate, createdDate);
this.accountId = accountId;
this.invoiceNumber = invoiceNumber;
@@ -67,7 +67,7 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
this.migrated = migrated;
this.isWrittenOff = false;
this.status = status;
- this.parentInvoice = parentInvoice;
+ this.isParentInvoice = isParentInvoice;
}
public InvoiceModelDao(final UUID accountId, final LocalDate invoiceDate, final LocalDate targetDate, final Currency currency, final boolean migrated) {
@@ -148,7 +148,7 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
}
public boolean isParentInvoice() {
- return parentInvoice;
+ return isParentInvoice;
}
public void setAccountId(final UUID accountId) {
@@ -195,10 +195,18 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
this.status = status;
}
- public void setParentInvoice(final boolean parentInvoice) {
+ public void setParentInvoice(final boolean isParentInvoice) {
+ this.isParentInvoice = isParentInvoice;
+ }
+
+ public void addParentInvoice(InvoiceModelDao parentInvoice) {
this.parentInvoice = parentInvoice;
}
+ public InvoiceModelDao getParentInvoice() {
+ return parentInvoice;
+ }
+
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("InvoiceModelDao{");
@@ -213,6 +221,7 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
sb.append(", invoicePayments=").append(invoicePayments);
sb.append(", processedCurrency=").append(processedCurrency);
sb.append(", isWrittenOff=").append(isWrittenOff);
+ sb.append(", isParentInvoice=").append(isParentInvoice);
sb.append(", parentInvoice=").append(parentInvoice);
sb.append('}');
return sb.toString();
@@ -262,7 +271,10 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
if (invoicePayments != null ? !invoicePayments.equals(that.invoicePayments) : that.invoicePayments != null) {
return false;
}
- if (parentInvoice != that.parentInvoice) {
+ if (isParentInvoice != that.isParentInvoice) {
+ return false;
+ }
+ if (parentInvoice != null ? !parentInvoice.equals(that.parentInvoice) : that.parentInvoice != null) {
return false;
}
return processedCurrency == that.processedCurrency;
@@ -282,7 +294,8 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
result = 31 * result + (invoicePayments != null ? invoicePayments.hashCode() : 0);
result = 31 * result + (processedCurrency != null ? processedCurrency.hashCode() : 0);
result = 31 * result + (isWrittenOff ? 1 : 0);
- result = 31 * result + (parentInvoice ? 1 : 0);
+ result = 31 * result + (isParentInvoice ? 1 : 0);
+ result = 31 * result + (parentInvoice != null ? parentInvoice.hashCode() : 0);
return result;
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
index a9c7f06..454c33f 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
@@ -26,7 +26,6 @@ import javax.annotation.Nullable;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
-
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.entity.EntityBase;
import org.killbill.billing.invoice.api.Invoice;
@@ -56,7 +55,8 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
private final Currency processedCurrency;
private final InvoiceStatus status;
- private final boolean parentInvoice;
+ private final boolean isParentInvoice;
+ private final Invoice parentInvoice;
// Used to create a new invoice
@@ -70,7 +70,7 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
public DefaultInvoice(final UUID invoiceId, final UUID accountId, @Nullable final Integer invoiceNumber, final LocalDate invoiceDate,
final LocalDate targetDate, final Currency currency, final boolean isMigrationInvoice, final InvoiceStatus status) {
- this(invoiceId, null, accountId, invoiceNumber, invoiceDate, targetDate, currency, currency, isMigrationInvoice, false, status, false);
+ this(invoiceId, null, accountId, invoiceNumber, invoiceDate, targetDate, currency, currency, isMigrationInvoice, false, status, false, null);
}
@@ -79,7 +79,8 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
this(invoiceModelDao.getId(), invoiceModelDao.getCreatedDate(), invoiceModelDao.getAccountId(),
invoiceModelDao.getInvoiceNumber(), invoiceModelDao.getInvoiceDate(), invoiceModelDao.getTargetDate(),
invoiceModelDao.getCurrency(), invoiceModelDao.getProcessedCurrency(), invoiceModelDao.isMigrated(),
- invoiceModelDao.isWrittenOff(), invoiceModelDao.getStatus(), invoiceModelDao.isParentInvoice());
+ invoiceModelDao.isWrittenOff(), invoiceModelDao.getStatus(), invoiceModelDao.isParentInvoice(),
+ invoiceModelDao.getParentInvoice());
addInvoiceItems(Collections2.transform(invoiceModelDao.getInvoiceItems(), new Function<InvoiceItemModelDao, InvoiceItem>() {
@Override
public InvoiceItem apply(final InvoiceItemModelDao input) {
@@ -96,14 +97,14 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
// Used to create a new parent invoice
public DefaultInvoice(final UUID accountId, final LocalDate invoiceDate, final Currency currency) {
- this(UUID.randomUUID(), null, accountId, null, invoiceDate, null, currency, currency, false, false, InvoiceStatus.DRAFT, true);
+ this(UUID.randomUUID(), null, accountId, null, invoiceDate, null, currency, currency, false, false, InvoiceStatus.DRAFT, true, null);
}
private DefaultInvoice(final UUID invoiceId, @Nullable final DateTime createdDate, final UUID accountId,
@Nullable final Integer invoiceNumber, final LocalDate invoiceDate,
@Nullable final LocalDate targetDate, final Currency currency, final Currency processedCurrency,
final boolean isMigrationInvoice, final boolean isWrittenOff,
- final InvoiceStatus status, final boolean parentInvoice) {
+ final InvoiceStatus status, final boolean isParentInvoice, final InvoiceModelDao parentInvoice) {
super(invoiceId, createdDate, createdDate);
this.accountId = accountId;
this.invoiceNumber = invoiceNumber;
@@ -116,14 +117,16 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
this.invoiceItems = new ArrayList<InvoiceItem>();
this.payments = new ArrayList<InvoicePayment>();
this.status = status;
- this.parentInvoice = parentInvoice;
+ this.isParentInvoice = isParentInvoice;
+ this.parentInvoice = (parentInvoice != null) ? new DefaultInvoice(parentInvoice) : null;
}
// Semi deep copy where we copy the lists but not the elements in the lists since they are immutables.
@Override
public Object clone() {
- final Invoice clonedInvoice = new DefaultInvoice(getId(), getCreatedDate(), getAccountId(), getInvoiceNumber(), getInvoiceDate(), getTargetDate(), getCurrency(), getProcessedCurrency(), isMigrationInvoice(), isWrittenOff(), getStatus(), isParentInvoice());
+ InvoiceModelDao parentInvoiceModelDao = (parentInvoice != null) ? new InvoiceModelDao(parentInvoice) : null;
+ final Invoice clonedInvoice = new DefaultInvoice(getId(), getCreatedDate(), getAccountId(), getInvoiceNumber(), getInvoiceDate(), getTargetDate(), getCurrency(), getProcessedCurrency(), isMigrationInvoice(), isWrittenOff(), getStatus(), isParentInvoice(), parentInvoiceModelDao);
clonedInvoice.getInvoiceItems().addAll(getInvoiceItems());
clonedInvoice.getPayments().addAll(getPayments());
return clonedInvoice;
@@ -246,7 +249,11 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
@Override
public BigDecimal getBalance() {
- return isWrittenOff ? BigDecimal.ZERO : InvoiceCalculatorUtils.computeInvoiceBalance(currency, invoiceItems, payments);
+ return isWrittenOff || hasZeroParentBalance() ? BigDecimal.ZERO : InvoiceCalculatorUtils.computeInvoiceBalance(currency, invoiceItems, payments);
+ }
+
+ private boolean hasZeroParentBalance() {
+ return (parentInvoice != null) && (parentInvoice.getBalance().compareTo(BigDecimal.ZERO) == 0);
}
public boolean isWrittenOff() {
@@ -260,14 +267,14 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
@Override
public boolean isParentInvoice() {
- return parentInvoice;
+ return isParentInvoice;
}
@Override
public String toString() {
return "DefaultInvoice [items=" + invoiceItems + ", payments=" + payments + ", id=" + id + ", accountId=" + accountId
+ ", invoiceDate=" + invoiceDate + ", targetDate=" + targetDate + ", currency=" + currency + ", amountPaid=" + getPaidAmount()
- + ", status=" + status + ", isParentInvoice=" + parentInvoice + "]";
+ + ", status=" + status + ", isParentInvoice=" + isParentInvoice + "]";
}
}