killbill-memoizeit
Changes
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationParentInvoice.java 11(+11 -0)
Details
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationParentInvoice.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationParentInvoice.java
index 2b9462e..7724af0 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationParentInvoice.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationParentInvoice.java
@@ -31,6 +31,7 @@ import org.killbill.billing.catalog.api.ProductCategory;
import org.killbill.billing.entitlement.api.DefaultEntitlement;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceApiException;
+import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.payment.api.Payment;
@@ -121,6 +122,16 @@ public class TestIntegrationParentInvoice extends TestIntegrationBase {
assertTrue(parentInvoice.getBalance().compareTo(BigDecimal.ZERO) == 0);
assertTrue(child1Invoices.get(1).getBalance().compareTo(BigDecimal.ZERO) == 0);
+ // load children invoice items
+ final List<InvoiceItem> childrenInvoiceItems = invoiceUserApi.getInvoiceItemsByParentInvoice(parentInvoice.getId(), callContext);
+ assertEquals(childrenInvoiceItems.size(), 2);
+ assertEquals(childrenInvoiceItems.get(0).getAccountId(), child1Account.getId());
+ assertEquals(childrenInvoiceItems.get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+ assertEquals(childrenInvoiceItems.get(1).getAccountId(), child2Account.getId());
+ assertEquals(childrenInvoiceItems.get(1).getAmount().compareTo(BigDecimal.valueOf(29.95)), 0);
+
+ // loading children items from non parent account should return empty list
+ assertEquals(invoiceUserApi.getInvoiceItemsByParentInvoice(child1Invoices.get(1).getId(), callContext).size(), 0);
}
@Test(groups = "slow")
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/logging/EntitlementLoggingHelper.java b/entitlement/src/main/java/org/killbill/billing/entitlement/logging/EntitlementLoggingHelper.java
index 20c886d..743cc8a 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/logging/EntitlementLoggingHelper.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/logging/EntitlementLoggingHelper.java
@@ -42,7 +42,7 @@ public abstract class EntitlementLoggingHelper {
if (log.isInfoEnabled()) {
final StringBuilder logLine = new StringBuilder("Create ")
- .append(bundleId != null ? " BP " : " AO ")
+ .append(bundleId != null ? " AO " : " BP ")
.append("Entitlement: ");
if (bundleId != null) {
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
index d9cbe1c..908e709 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -556,4 +556,16 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
dao.transferChildCreditToParent(childAccount, internalCallContext);
}
+
+ @Override
+ public List<InvoiceItem> getInvoiceItemsByParentInvoice(final UUID parentInvoiceId, final TenantContext context) throws InvoiceApiException {
+ final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(parentInvoiceId, ObjectType.INVOICE, context);
+ return ImmutableList.copyOf(Collections2.transform(dao.getInvoiceItemsByParentInvoice(parentInvoiceId, internalTenantContext),
+ new Function<InvoiceItemModelDao, InvoiceItem>() {
+ @Override
+ public InvoiceItem apply(final InvoiceItemModelDao input) {
+ return InvoiceItemFactory.fromModelDao(input);
+ }
+ }));
+ }
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
index 5ee6b8e..204a0e5 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
@@ -1175,4 +1175,15 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
}
});
}
+
+ @Override
+ public List<InvoiceItemModelDao> getInvoiceItemsByParentInvoice(final UUID parentInvoiceId, final InternalTenantContext context) throws InvoiceApiException {
+ return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceItemModelDao>>() {
+ @Override
+ public List<InvoiceItemModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+ final InvoiceItemSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
+ return transactional.getInvoiceItemsByParentInvoice(parentInvoiceId.toString(), context);
+ }
+ });
+ }
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
index d1d16f1..ca7c49b 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
@@ -203,4 +203,14 @@ public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceA
* @throws InvoiceApiException if any unexpected error occurs
*/
void transferChildCreditToParent(Account childAccount, InternalCallContext childAccountContext) throws InvoiceApiException;
+
+ /**
+ * Retrieve invoice items details associated to Parent SUMMARY invoice item
+ *
+ * @param parentInvoiceId the parent invoice id
+ * @param context the tenant context
+ * @return a list of invoice items associated with a parent invoice
+ * @throws InvoiceApiException if any unexpected error occurs
+ */
+ List<InvoiceItemModelDao> getInvoiceItemsByParentInvoice(UUID parentInvoiceId, final InternalTenantContext context) throws InvoiceApiException;
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.java
index 8278a98..e30b5d2 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.java
@@ -53,4 +53,8 @@ public interface InvoiceItemSqlDao extends EntitySqlDao<InvoiceItemModelDao, Inv
void updateAmount(@Bind("id") String invoiceItemId,
@Bind("amount")BigDecimal amount,
@BindBean final InternalCallContext context);
+
+ @SqlQuery
+ List<InvoiceItemModelDao> getInvoiceItemsByParentInvoice(@Bind("parentInvoiceId") final String parentInvoiceId,
+ @BindBean final InternalTenantContext context);
}
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.sql.stg b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
index 0542071..ae19a63 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
@@ -75,4 +75,14 @@ updateAmount() ::= <<
SET amount = :amount
WHERE id = :id
<AND_CHECK_TENANT()>;
+>>
+
+getInvoiceItemsByParentInvoice() ::= <<
+ SELECT <allTableFields(("items."))>
+ FROM <tableName()> items
+ INNER JOIN invoice_parent_children invRel ON invRel.child_invoice_id = items.invoice_id
+ WHERE invRel.parent_invoice_id = :parentInvoiceId
+ <AND_CHECK_TENANT("items.")>
+ <defaultOrderBy()>
+ ;
>>
\ No newline at end of file
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
index 9148a33..8ba0cc8 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
@@ -405,4 +405,9 @@ public class MockInvoiceDao extends MockEntityDaoBase<InvoiceModelDao, Invoice,
public void transferChildCreditToParent(final Account childAccount, final InternalCallContext context) throws InvoiceApiException {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public List<InvoiceItemModelDao> getInvoiceItemsByParentInvoice(final UUID parentInvoiceId, final InternalTenantContext context) throws InvoiceApiException {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
index 7bb3b93..b26dbbb 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
@@ -63,6 +63,7 @@ import org.killbill.billing.invoice.model.DefaultInvoice;
import org.killbill.billing.invoice.model.DefaultInvoicePayment;
import org.killbill.billing.invoice.model.ExternalChargeInvoiceItem;
import org.killbill.billing.invoice.model.FixedPriceInvoiceItem;
+import org.killbill.billing.invoice.model.ItemAdjInvoiceItem;
import org.killbill.billing.invoice.model.ParentInvoiceItem;
import org.killbill.billing.invoice.model.RecurringInvoiceItem;
import org.killbill.billing.invoice.model.RepairAdjInvoiceItem;
@@ -1761,4 +1762,33 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
}
+ @Test(groups = "slow")
+ public void testRetrieveInvoiceItemsByParentInvoice() throws InvoiceApiException {
+ final UUID childAccountId = account.getId();
+ final Invoice childInvoice = new DefaultInvoice(childAccountId, clock.getUTCToday(), clock.getUTCToday(), Currency.USD);
+ final UUID invoiceId = childInvoice.getId();
+ final UUID subscriptionId = UUID.randomUUID();
+ final UUID bundleId = UUID.randomUUID();
+ final LocalDate startDate = new LocalDate(2010, 1, 1);
+ final LocalDate endDate = new LocalDate(2010, 4, 1);
+ final InvoiceItem invoiceItem = new RecurringInvoiceItem(invoiceId, childAccountId, bundleId, subscriptionId, "test plan", "test phase", startDate, endDate,
+ new BigDecimal("21.00"), new BigDecimal("7.00"), Currency.USD);
+ final InvoiceItem invoiceAdj = new ItemAdjInvoiceItem(invoiceItem, startDate, new BigDecimal("-5.00"), Currency.USD);
+
+ childInvoice.addInvoiceItem(invoiceItem);
+ childInvoice.addInvoiceItem(invoiceAdj);
+ invoiceUtil.createInvoice(childInvoice, true, context);
+
+ final UUID parentInvoiceId = UUID.randomUUID();
+
+ InvoiceParentChildModelDao invoiceRelation = new InvoiceParentChildModelDao(parentInvoiceId, childInvoice.getId(), childAccountId);
+ invoiceDao.createParentChildInvoiceRelation(invoiceRelation, context);
+
+ final List<InvoiceItemModelDao> invoiceItems = invoiceDao.getInvoiceItemsByParentInvoice(parentInvoiceId, context);
+ assertEquals(invoiceItems.size(), 2);
+ assertEquals(invoiceItems.get(0).getType(), InvoiceItemType.RECURRING);
+ assertEquals(invoiceItems.get(1).getType(), InvoiceItemType.ITEM_ADJ);
+
+ }
+
}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceItemJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceItemJson.java
index 55bf4d5..a363c25 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceItemJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceItemJson.java
@@ -31,6 +31,9 @@ import org.killbill.billing.util.audit.AuditLog;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
import io.swagger.annotations.ApiModelProperty;
public class InvoiceItemJson extends JsonBase {
@@ -58,6 +61,7 @@ public class InvoiceItemJson extends JsonBase {
private final LocalDate endDate;
private final BigDecimal amount;
private final String currency;
+ private List<InvoiceItemJson> childItems;
@JsonCreator
public InvoiceItemJson(@JsonProperty("invoiceItemId") final String invoiceItemId,
@@ -76,6 +80,7 @@ public class InvoiceItemJson extends JsonBase {
@JsonProperty("endDate") final LocalDate endDate,
@JsonProperty("amount") final BigDecimal amount,
@JsonProperty("currency") final String currency,
+ @JsonProperty("childItems") final List<InvoiceItemJson> childItems,
@JsonProperty("auditLogs") @Nullable final List<AuditLogJson> auditLogs) {
super(auditLogs);
this.invoiceItemId = invoiceItemId;
@@ -94,14 +99,27 @@ public class InvoiceItemJson extends JsonBase {
this.endDate = endDate;
this.amount = amount;
this.currency = currency;
+ this.childItems = childItems;
}
- public InvoiceItemJson(final InvoiceItem item, @Nullable final List<AuditLog> auditLogs) {
+ public InvoiceItemJson(final InvoiceItem item, final List<InvoiceItem> childItems, @Nullable final List<AuditLog> auditLogs) {
this(toString(item.getId()), toString(item.getInvoiceId()), toString(item.getLinkedItemId()),
toString(item.getAccountId()), toString(item.getChildAccountId()), toString(item.getBundleId()), toString(item.getSubscriptionId()),
item.getPlanName(), item.getPhaseName(), item.getUsageName(), item.getInvoiceItemType().toString(),
item.getDescription(), item.getStartDate(), item.getEndDate(),
- item.getAmount(), item.getCurrency().name(), toAuditLogJson(auditLogs));
+ item.getAmount(), item.getCurrency().name(), toInvoiceItemJson(childItems), toAuditLogJson(auditLogs));
+ }
+
+ private static List<InvoiceItemJson> toInvoiceItemJson(final List<InvoiceItem> childItems) {
+ if (childItems == null) {
+ return null;
+ }
+ return ImmutableList.copyOf(Collections2.transform(childItems, new Function<InvoiceItem, InvoiceItemJson>() {
+ @Override
+ public InvoiceItemJson apply(final InvoiceItem input) {
+ return new InvoiceItemJson(input);
+ }
+ }));
}
public InvoiceItem toInvoiceItem() {
@@ -209,7 +227,7 @@ public class InvoiceItemJson extends JsonBase {
}
public InvoiceItemJson(final InvoiceItem input) {
- this(input, null);
+ this(input, null, null);
}
public String getInvoiceItemId() {
@@ -276,6 +294,10 @@ public class InvoiceItemJson extends JsonBase {
return currency;
}
+ public List<InvoiceItemJson> getChildItems() {
+ return childItems;
+ }
+
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
@@ -295,6 +317,7 @@ public class InvoiceItemJson extends JsonBase {
sb.append(", endDate=").append(endDate);
sb.append(", amount=").append(amount);
sb.append(", currency=").append(currency);
+ sb.append(", childItems=").append(childItems);
sb.append('}');
return sb.toString();
}
@@ -358,6 +381,9 @@ public class InvoiceItemJson extends JsonBase {
if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null) {
return false;
}
+ if (childItems != null ? !childItems.equals(that.childItems) : that.childItems != null) {
+ return false;
+ }
return true;
}
@@ -379,6 +405,7 @@ public class InvoiceItemJson extends JsonBase {
result = 31 * result + (endDate != null ? endDate.hashCode() : 0);
result = 31 * result + (amount != null ? amount.hashCode() : 0);
result = 31 * result + (currency != null ? currency.hashCode() : 0);
+ result = 31 * result + (childItems != null ? childItems.hashCode() : 0);
return result;
}
}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceJson.java
index 9ef5ac9..7b002a3 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceJson.java
@@ -22,15 +22,19 @@ import java.util.List;
import javax.annotation.Nullable;
+import org.apache.shiro.util.CollectionUtils;
import org.joda.time.LocalDate;
-
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceItem;
+import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.util.audit.AccountAuditLogs;
import org.killbill.billing.util.audit.AuditLog;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import io.swagger.annotations.ApiModelProperty;
public class InvoiceJson extends JsonBase {
@@ -89,7 +93,7 @@ public class InvoiceJson extends JsonBase {
}
public InvoiceJson(final Invoice input) {
- this(input, false, null);
+ this(input, false, null, null);
}
public InvoiceJson(final Invoice input, final String bundleKeys, final List<CreditJson> credits, final List<AuditLog> auditLogs) {
@@ -98,12 +102,21 @@ public class InvoiceJson extends JsonBase {
input.getBalance(), input.getAccountId().toString(), bundleKeys, credits, null, input.isParentInvoice(), toAuditLogJson(auditLogs));
}
- public InvoiceJson(final Invoice input, final boolean withItems, @Nullable final AccountAuditLogs accountAuditLogs) {
+ public InvoiceJson(final Invoice input, final boolean withItems, final List<InvoiceItem> childItems, @Nullable final AccountAuditLogs accountAuditLogs) {
super(toAuditLogJson(accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForInvoice(input.getId())));
this.items = new ArrayList<InvoiceItemJson>(input.getInvoiceItems().size());
- if (withItems) {
+ if (withItems || !CollectionUtils.isEmpty(childItems)) {
for (final InvoiceItem item : input.getInvoiceItems()) {
- this.items.add(new InvoiceItemJson(item, accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForInvoiceItem(item.getId())));
+ ImmutableList<InvoiceItem> childItemsFiltered = null;
+ if (item.getInvoiceItemType().equals(InvoiceItemType.PARENT_SUMMARY) && !CollectionUtils.isEmpty(childItems)) {
+ childItemsFiltered = ImmutableList.copyOf(Iterables.filter(childItems, new Predicate<InvoiceItem>() {
+ @Override
+ public boolean apply(@Nullable final InvoiceItem invoice) {
+ return invoice.getAccountId().equals(item.getChildAccountId());
+ }
+ }));
+ }
+ this.items.add(new InvoiceItemJson(item, childItemsFiltered, accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForInvoiceItem(item.getId())));
}
}
this.amount = input.getChargedAmount();
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
index b97a2e8..83e5192 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
@@ -614,7 +614,7 @@ public class AccountResource extends JaxRsResourceBase {
final List<InvoiceJson> result = new LinkedList<InvoiceJson>();
for (final Invoice invoice : invoices) {
- result.add(new InvoiceJson(invoice, withItems, accountAuditLogs));
+ result.add(new InvoiceJson(invoice, withItems, null, accountAuditLogs));
}
return Response.status(Status.OK).entity(result).build();
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
index cb7a00d..fcda3be 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
@@ -174,16 +174,18 @@ public class InvoiceResource extends JaxRsResourceBase {
@ApiResponse(code = 404, message = "Invoice not found")})
public Response getInvoice(@PathParam("invoiceId") final String invoiceId,
@QueryParam(QUERY_INVOICE_WITH_ITEMS) @DefaultValue("false") final boolean withItems,
+ @QueryParam(QUERY_INVOICE_WITH_CHILDREN_ITEMS) @DefaultValue("false") final boolean withChildrenItems,
@QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
@javax.ws.rs.core.Context final HttpServletRequest request) throws InvoiceApiException {
final TenantContext tenantContext = context.createContext(request);
final Invoice invoice = invoiceApi.getInvoice(UUID.fromString(invoiceId), tenantContext);
+ final List<InvoiceItem> childInvoiceItems = withChildrenItems ? invoiceApi.getInvoiceItemsByParentInvoice(invoice.getId(), tenantContext) : null;
final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(invoice.getAccountId(), auditMode.getLevel(), tenantContext);
if (invoice == null) {
throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceId);
} else {
- final InvoiceJson json = new InvoiceJson(invoice, withItems, accountAuditLogs);
+ final InvoiceJson json = new InvoiceJson(invoice, withItems, childInvoiceItems, accountAuditLogs);
return Response.status(Status.OK).entity(json).build();
}
}
@@ -196,16 +198,18 @@ public class InvoiceResource extends JaxRsResourceBase {
@ApiResponses(value = {@ApiResponse(code = 404, message = "Invoice not found")})
public Response getInvoiceByNumber(@PathParam("invoiceNumber") final Integer invoiceNumber,
@QueryParam(QUERY_INVOICE_WITH_ITEMS) @DefaultValue("false") final boolean withItems,
+ @QueryParam(QUERY_INVOICE_WITH_CHILDREN_ITEMS) @DefaultValue("false") final boolean withChildrenItems,
@QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
@javax.ws.rs.core.Context final HttpServletRequest request) throws InvoiceApiException {
final TenantContext tenantContext = context.createContext(request);
final Invoice invoice = invoiceApi.getInvoiceByNumber(invoiceNumber, tenantContext);
+ final List<InvoiceItem> childInvoiceItems = withChildrenItems ? invoiceApi.getInvoiceItemsByParentInvoice(invoice.getId(), tenantContext) : null;
final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(invoice.getAccountId(), auditMode.getLevel(), tenantContext);
if (invoice == null) {
throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceNumber);
} else {
- final InvoiceJson json = new InvoiceJson(invoice, withItems, accountAuditLogs);
+ final InvoiceJson json = new InvoiceJson(invoice, withItems, childInvoiceItems, accountAuditLogs);
return Response.status(Status.OK).entity(json).build();
}
}
@@ -246,7 +250,7 @@ public class InvoiceResource extends JaxRsResourceBase {
if (accountsAuditLogs.get().get(invoice.getAccountId()) == null) {
accountsAuditLogs.get().put(invoice.getAccountId(), auditUserApi.getAccountAuditLogs(invoice.getAccountId(), auditMode.getLevel(), tenantContext));
}
- return new InvoiceJson(invoice, withItems, accountsAuditLogs.get().get(invoice.getAccountId()));
+ return new InvoiceJson(invoice, withItems, null, accountsAuditLogs.get().get(invoice.getAccountId()));
}
},
nextPageUri
@@ -279,7 +283,7 @@ public class InvoiceResource extends JaxRsResourceBase {
if (accountsAuditLogs.get().get(invoice.getAccountId()) == null) {
accountsAuditLogs.get().put(invoice.getAccountId(), auditUserApi.getAccountAuditLogs(invoice.getAccountId(), auditMode.getLevel(), tenantContext));
}
- return new InvoiceJson(invoice, withItems, accountsAuditLogs.get().get(invoice.getAccountId()));
+ return new InvoiceJson(invoice, withItems, null, accountsAuditLogs.get().get(invoice.getAccountId()));
}
},
nextPageUri
@@ -393,7 +397,7 @@ public class InvoiceResource extends JaxRsResourceBase {
try {
final Invoice generatedInvoice = invoiceApi.triggerInvoiceGeneration(UUID.fromString(accountId), inputDate, dryRunArguments,
callContext);
- return Response.status(Status.OK).entity(new InvoiceJson(generatedInvoice, true, null)).build();
+ return Response.status(Status.OK).entity(new InvoiceJson(generatedInvoice, true, null, null)).build();
} catch (InvoiceApiException e) {
if (e.getCode() == ErrorCode.INVOICE_NOTHING_TO_DO.getCode()) {
return Response.status(Status.NOT_FOUND).build();
@@ -550,6 +554,7 @@ public class InvoiceResource extends JaxRsResourceBase {
input.getEndDate(),
input.getAmount(),
accountCurrency.name(),
+ null,
null);
}
}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
index 3bd3536..c4f18d3 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
@@ -94,6 +94,7 @@ public interface JaxrsResource {
public static final String QUERY_INVOICE_WITH_ITEMS = "withItems";
public static final String QUERY_WITH_MIGRATION_INVOICES = "withMigrationInvoices";
public static final String QUERY_UNPAID_INVOICES_ONLY = "unpaidInvoicesOnly";
+ public static final String QUERY_INVOICE_WITH_CHILDREN_ITEMS = "withChildrenItems";
public static final String QUERY_PAYMENT_EXTERNAL = "externalPayment";
public static final String QUERY_PAYMENT_AMOUNT = "paymentAmount";
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceItemJsonSimple.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceItemJsonSimple.java
index b4b33f9..bd54e91 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceItemJsonSimple.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceItemJsonSimple.java
@@ -55,7 +55,7 @@ public class TestInvoiceItemJsonSimple extends JaxrsTestSuiteNoDB {
final List<AuditLogJson> auditLogs = createAuditLogsJson(clock.getUTCNow());
final InvoiceItemJson invoiceItemJson = new InvoiceItemJson(invoiceItemId, invoiceId, linkedInvoiceItemId, accountId, childAccountId,
bundleId, subscriptionId, planName, phaseName, usageName, type, description,
- startDate, endDate, amount, currency.name(), auditLogs);
+ startDate, endDate, amount, currency.name(), null, auditLogs);
Assert.assertEquals(invoiceItemJson.getInvoiceItemId(), invoiceItemId);
Assert.assertEquals(invoiceItemJson.getInvoiceId(), invoiceId);
Assert.assertEquals(invoiceItemJson.getLinkedInvoiceItemId(), linkedInvoiceItemId);
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
index 7c85225..424b1dc 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
@@ -42,8 +42,8 @@ import org.killbill.billing.client.model.Invoices;
import org.killbill.billing.client.model.PaymentMethod;
import org.killbill.billing.entitlement.api.SubscriptionEventType;
import org.killbill.billing.invoice.api.DryRunType;
-import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.payment.provider.ExternalPaymentProviderPlugin;
import org.killbill.billing.util.api.AuditLevel;
import org.testng.Assert;
@@ -765,4 +765,93 @@ public class TestInvoice extends TestJaxrsBase {
}
+ @Test(groups = "slow", description = "Can search and retrieve parent and children invoices with and without children items")
+ public void testParentInvoiceWithChildItems() throws Exception {
+ final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+ clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+ final Account parentAccount = createAccount();
+ final Account childAccount1 = createAccount(parentAccount.getAccountId());
+ final Account childAccount2 = createAccount(parentAccount.getAccountId());
+ final Account childAccount3 = createAccount(parentAccount.getAccountId());
+
+ // Add a bundle, subscription and move the clock to get the first invoice
+ createEntitlement(childAccount1.getAccountId(), UUID.randomUUID().toString(), "Shotgun",
+ ProductCategory.BASE, BillingPeriod.MONTHLY, true);
+ createEntitlement(childAccount2.getAccountId(), UUID.randomUUID().toString(), "Pistol",
+ ProductCategory.BASE, BillingPeriod.MONTHLY, true);
+ createEntitlement(childAccount3.getAccountId(), UUID.randomUUID().toString(), "Shotgun",
+ ProductCategory.BASE, BillingPeriod.MONTHLY, true);
+
+
+ clock.addDays(32);
+ crappyWaitForLackOfProperSynchonization();
+
+ final List<Invoice> child1Invoices = killBillClient.getInvoicesForAccount(childAccount1.getAccountId(), true, false, requestOptions);
+ final List<Invoice> child2Invoices = killBillClient.getInvoicesForAccount(childAccount2.getAccountId(), true, false, requestOptions);
+ final List<Invoice> child3Invoices = killBillClient.getInvoicesForAccount(childAccount3.getAccountId(), true, false, requestOptions);
+
+ assertEquals(child1Invoices.size(), 2);
+ final Invoice child1RecurringInvoice = child1Invoices.get(1);
+ final InvoiceItem child1RecurringInvoiceItem = child1RecurringInvoice.getItems().get(0);
+ final InvoiceItem child2RecurringInvoiceItem = child2Invoices.get(1).getItems().get(0);
+ final InvoiceItem child3RecurringInvoiceItem = child3Invoices.get(1).getItems().get(0);
+
+ final List<Invoice> parentInvoices = killBillClient.getInvoicesForAccount(parentAccount.getAccountId(), true, false, requestOptions);
+ assertEquals(parentInvoices.size(), 2);
+
+ // check parent invoice with child invoice items and no adjustments
+ // parameters: withItems = true, withChildrenItems = true
+ Invoice parentInvoiceWithChildItems = killBillClient.getInvoice(parentInvoices.get(1).getInvoiceId(), true, true, requestOptions);
+ assertEquals(parentInvoiceWithChildItems.getItems().size(), 3);
+ assertEquals(parentInvoiceWithChildItems.getItems().get(0).getChildItems().size(), 1);
+ assertEquals(parentInvoiceWithChildItems.getItems().get(1).getChildItems().size(), 1);
+ assertEquals(parentInvoiceWithChildItems.getItems().get(2).getChildItems().size(), 1);
+
+ // add an item adjustment
+ final InvoiceItem adjustmentInvoiceItem = new InvoiceItem();
+ adjustmentInvoiceItem.setAccountId(childAccount1.getAccountId());
+ adjustmentInvoiceItem.setInvoiceId(child1RecurringInvoice.getInvoiceId());
+ adjustmentInvoiceItem.setInvoiceItemId(child1RecurringInvoiceItem.getInvoiceItemId());
+ adjustmentInvoiceItem.setAmount(BigDecimal.TEN);
+ adjustmentInvoiceItem.setCurrency(child1RecurringInvoiceItem.getCurrency());
+ final Invoice invoiceAdjustment = killBillClient.adjustInvoiceItem(adjustmentInvoiceItem, requestOptions);
+ final InvoiceItem child1AdjInvoiceItem = killBillClient.getInvoice(invoiceAdjustment.getInvoiceId(), requestOptions).getItems().get(1);
+
+ // check parent invoice with child invoice items and adjustments
+ // parameters: withItems = true, withChildrenItems = true
+ parentInvoiceWithChildItems = killBillClient.getInvoice(parentInvoices.get(1).getInvoiceId(), true, true, requestOptions);
+ assertEquals(parentInvoiceWithChildItems.getItems().size(), 3);
+ assertEquals(parentInvoiceWithChildItems.getItems().get(0).getChildItems().size(), 2);
+ assertEquals(parentInvoiceWithChildItems.getItems().get(1).getChildItems().size(), 1);
+ assertEquals(parentInvoiceWithChildItems.getItems().get(2).getChildItems().size(), 1);
+
+ final InvoiceItem child1InvoiceItemFromParent = parentInvoiceWithChildItems.getItems().get(0).getChildItems().get(0);
+ final InvoiceItem child1AdjInvoiceItemFromParent = parentInvoiceWithChildItems.getItems().get(0).getChildItems().get(1);
+ final InvoiceItem child2InvoiceItemFromParent = parentInvoiceWithChildItems.getItems().get(1).getChildItems().get(0);
+ final InvoiceItem child3InvoiceItemFromParent = parentInvoiceWithChildItems.getItems().get(2).getChildItems().get(0);
+
+ // check children items for each PARENT_SUMMARY item
+ assertTrue(child1InvoiceItemFromParent.equals(child1RecurringInvoiceItem));
+ assertTrue(child1AdjInvoiceItemFromParent.equals(child1AdjInvoiceItem));
+ assertTrue(child2InvoiceItemFromParent.equals(child2RecurringInvoiceItem));
+ assertTrue(child3InvoiceItemFromParent.equals(child3RecurringInvoiceItem));
+
+ // check parent invoice without child invoice items
+ parentInvoiceWithChildItems = killBillClient.getInvoice(parentInvoices.get(1).getInvoiceId(), true, false, requestOptions);
+ assertEquals(parentInvoiceWithChildItems.getItems().size(), 3);
+ assertNull(parentInvoiceWithChildItems.getItems().get(0).getChildItems());
+ assertNull(parentInvoiceWithChildItems.getItems().get(1).getChildItems());
+ assertNull(parentInvoiceWithChildItems.getItems().get(2).getChildItems());
+
+ // check parent invoice without items but with child invoice items and adjustment. Should return items anyway.
+ // parameters: withItems = false, withChildrenItems = true
+ parentInvoiceWithChildItems = killBillClient.getInvoice(parentInvoices.get(1).getInvoiceId(), false, true, requestOptions);
+ assertEquals(parentInvoiceWithChildItems.getItems().size(), 3);
+ assertEquals(parentInvoiceWithChildItems.getItems().get(0).getChildItems().size(), 2);
+ assertEquals(parentInvoiceWithChildItems.getItems().get(1).getChildItems().size(), 1);
+ assertEquals(parentInvoiceWithChildItems.getItems().get(2).getChildItems().size(), 1);
+
+ }
+
}