killbill-aplcache
Changes
osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoiceDao.java 258(+252 -6)
osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceAdjustmentModelDao.java 2(+2 -0)
osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemAdjustmentModelDao.java 2(+2 -0)
osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemBaseModelDao.java 28(+19 -9)
osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemCreditModelDao.java 2(+2 -0)
osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemModelDao.java 2(+2 -0)
osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessModelDaoBase.java 3(+0 -3)
osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/AnalyticsTestSuiteNoDB.java 9(+9 -0)
osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessInvoice.java 2(+2 -0)
osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessInvoiceItem.java 2(+2 -0)
osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessSnapshot.java 2(+2 -0)
Details
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoiceDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoiceDao.java
index 8e89f8e..d67189e 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoiceDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoiceDao.java
@@ -16,27 +16,34 @@
package com.ning.billing.osgi.bundles.analytics.dao;
+import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
-import javax.annotation.Nullable;
-
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.osgi.service.log.LogService;
import org.skife.jdbi.v2.Transaction;
import org.skife.jdbi.v2.TransactionStatus;
import com.ning.billing.account.api.Account;
+import com.ning.billing.catalog.api.Currency;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
import com.ning.billing.osgi.bundles.analytics.AnalyticsRefreshException;
import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessAccountModelDao;
import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceItemBaseModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceItemBaseModelDao.BusinessInvoiceItemType;
import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceModelDao;
import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessModelDaoBase.ReportGroup;
import com.ning.billing.util.audit.AuditLog;
@@ -46,6 +53,10 @@ import com.ning.killbill.osgi.libs.killbill.OSGIKillbillAPI;
import com.ning.killbill.osgi.libs.killbill.OSGIKillbillDataSource;
import com.ning.killbill.osgi.libs.killbill.OSGIKillbillLogService;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+
public class BusinessInvoiceDao extends BusinessAnalyticsDaoBase {
private final BusinessAccountDao businessAccountDao;
@@ -109,13 +120,21 @@ public class BusinessInvoiceDao extends BusinessAnalyticsDaoBase {
tenantRecordId,
reportGroup);
+ final List<InvoiceItem> allInvoiceItems = invoice.getInvoiceItems();
+ final Collection<InvoiceItem> sanitizedInvoiceItems = sanitizeInvoiceItems(allInvoiceItems);
+
final List<BusinessInvoiceItemBaseModelDao> businessInvoiceItems = new ArrayList<BusinessInvoiceItemBaseModelDao>();
- for (final InvoiceItem invoiceItem : invoice.getInvoiceItems()) {
+ for (final InvoiceItem invoiceItem : sanitizedInvoiceItems) {
final BusinessInvoiceItemBaseModelDao businessInvoiceItem = createBusinessInvoiceItem(account,
invoice,
invoiceItem,
- // TODO Will be used for REPAIR_ADJ
- null,
+ Collections2.filter(sanitizedInvoiceItems,
+ new Predicate<InvoiceItem>() {
+ @Override
+ public boolean apply(final InvoiceItem input) {
+ return !input.getId().equals(invoiceItem.getId());
+ }
+ }),
context);
if (businessInvoiceItem != null) {
businessInvoiceItems.add(businessInvoiceItem);
@@ -168,7 +187,7 @@ public class BusinessInvoiceDao extends BusinessAnalyticsDaoBase {
private BusinessInvoiceItemBaseModelDao createBusinessInvoiceItem(final Account account,
final Invoice invoice,
final InvoiceItem invoiceItem,
- @Nullable final Long secondInvoiceItemRecordId,
+ final Collection<InvoiceItem> otherInvoiceItemsOnInvoice,
final TenantContext context) throws AnalyticsRefreshException {
SubscriptionBundle bundle = null;
// Subscription and bundle could be null for e.g. credits or adjustments
@@ -192,10 +211,35 @@ public class BusinessInvoiceDao extends BusinessAnalyticsDaoBase {
final Long tenantRecordId = getTenantRecordId(context);
final ReportGroup reportGroup = getReportGroup(account.getId(), context);
+ final BusinessInvoiceItemType businessInvoiceItemType;
+ if (isCharge(invoiceItem)) {
+ businessInvoiceItemType = BusinessInvoiceItemType.CHARGE;
+ } else if (isAccountCreditItem(invoiceItem)) {
+ businessInvoiceItemType = BusinessInvoiceItemType.ACCOUNT_CREDIT;
+ } else if (isInvoiceItemAdjustmentItem(invoiceItem)) {
+ businessInvoiceItemType = BusinessInvoiceItemType.INVOICE_ITEM_ADJUSTMENT;
+ } else if (isInvoiceAdjustmentItem(invoiceItem, otherInvoiceItemsOnInvoice)) {
+ businessInvoiceItemType = BusinessInvoiceItemType.INVOICE_ADJUSTMENT;
+ } else {
+ // We don't care
+ return null;
+ }
+
+ final Boolean revenueRecognizable = isRevenueRecognizable(invoiceItem);
+
+ final Long secondInvoiceItemRecordId;
+ if (invoiceItem instanceof AdjustmentInvoiceItemForRepair) {
+ secondInvoiceItemRecordId = getInvoiceItemRecordId(((AdjustmentInvoiceItemForRepair) invoiceItem).getSecondId(), context);
+ } else {
+ secondInvoiceItemRecordId = null;
+ }
+
return BusinessInvoiceItemBaseModelDao.create(account,
accountRecordId,
invoice,
invoiceItem,
+ revenueRecognizable,
+ businessInvoiceItemType,
invoiceItemRecordId,
secondInvoiceItemRecordId,
bundle,
@@ -205,4 +249,206 @@ public class BusinessInvoiceDao extends BusinessAnalyticsDaoBase {
tenantRecordId,
reportGroup);
}
+
+ @VisibleForTesting
+ Boolean isRevenueRecognizable(final InvoiceItem invoiceItem) {
+ // REFUND_ADJ are recognizable because associated with a payment
+ return !InvoiceItemType.CREDIT_ADJ.equals(invoiceItem.getInvoiceItemType());
+ }
+
+ // Invoice adjustments
+ @VisibleForTesting
+ boolean isInvoiceAdjustmentItem(final InvoiceItem invoiceItem, final Collection<InvoiceItem> otherInvoiceItemsOnInvoice) {
+ // Either REFUND_ADJ
+ return InvoiceItemType.REFUND_ADJ.equals(invoiceItem.getInvoiceItemType()) ||
+ // Or invoice level credit, i.e. credit adj, but NOT on its on own invoice
+ // Note: the negative credit adj items (internal generation of account level credits) doesn't figure in analytics
+ (InvoiceItemType.CREDIT_ADJ.equals(invoiceItem.getInvoiceItemType()) &&
+ !(otherInvoiceItemsOnInvoice.size() == 1 &&
+ InvoiceItemType.CBA_ADJ.equals(otherInvoiceItemsOnInvoice.iterator().next().getInvoiceItemType()) &&
+ otherInvoiceItemsOnInvoice.iterator().next().getAmount().compareTo(invoiceItem.getAmount().negate()) == 0));
+ }
+
+ // Item adjustments
+ private boolean isInvoiceItemAdjustmentItem(final InvoiceItem invoiceItem) {
+ return InvoiceItemType.ITEM_ADJ.equals(invoiceItem.getInvoiceItemType());
+ }
+
+ // Account credits, used or consumed
+ private boolean isAccountCreditItem(final InvoiceItem invoiceItem) {
+ return InvoiceItemType.CBA_ADJ.equals(invoiceItem.getInvoiceItemType());
+ }
+
+ // Regular line item (charges)
+ private boolean isCharge(final InvoiceItem invoiceItem) {
+ return InvoiceItemType.EXTERNAL_CHARGE.equals(invoiceItem.getInvoiceItemType()) ||
+ InvoiceItemType.FIXED.equals(invoiceItem.getInvoiceItemType()) ||
+ InvoiceItemType.RECURRING.equals(invoiceItem.getInvoiceItemType());
+ }
+
+ @VisibleForTesting
+ Collection<InvoiceItem> sanitizeInvoiceItems(final List<InvoiceItem> allInvoiceItems) {
+ // Build a convenience mapping between items -> repair_adj items (inverse of linkedItemId)
+ final Map<UUID, InvoiceItem> repairedInvoiceItemIdToRepairInvoiceItemMappings = new HashMap<UUID, InvoiceItem>();
+ for (final InvoiceItem invoiceItem : allInvoiceItems) {
+ if (InvoiceItemType.REPAIR_ADJ.equals(invoiceItem.getInvoiceItemType())) {
+ repairedInvoiceItemIdToRepairInvoiceItemMappings.put(invoiceItem.getLinkedItemId(), invoiceItem);
+ }
+ }
+
+ // Now find the "reparation" items, i.e. the ones which correspond to the repaired items
+ final Map<UUID, InvoiceItem> reparationInvoiceItemIdToRepairItemMappings = new LinkedHashMap<UUID, InvoiceItem>();
+ for (final InvoiceItem repairedInvoiceItem : allInvoiceItems) {
+ // Skip non-repaired items
+ if (!repairedInvoiceItemIdToRepairInvoiceItemMappings.keySet().contains(repairedInvoiceItem.getId())) {
+ continue;
+ }
+
+ InvoiceItem reparationItem = null;
+ for (final InvoiceItem invoiceItem : allInvoiceItems) {
+ // Try to find the matching "reparation" item
+ if (repairedInvoiceItem.getInvoiceItemType().equals(invoiceItem.getInvoiceItemType()) &&
+ repairedInvoiceItem.getSubscriptionId().equals(invoiceItem.getSubscriptionId()) &&
+ repairedInvoiceItem.getStartDate().compareTo(invoiceItem.getStartDate()) == 0 &&
+ repairedInvoiceItem.getEndDate().isAfter(invoiceItem.getEndDate())) {
+ if (reparationItem == null) {
+ reparationItem = invoiceItem;
+ } else {
+ logService.log(LogService.LOG_ERROR, "Found multiple reparation items matching the repair item id " + repairedInvoiceItem.getId() + " - this should never happen!");
+ }
+ }
+ }
+
+ if (reparationItem != null) {
+ reparationInvoiceItemIdToRepairItemMappings.put(reparationItem.getId(), repairedInvoiceItemIdToRepairInvoiceItemMappings.get(repairedInvoiceItem.getId()));
+ } else {
+ logService.log(LogService.LOG_ERROR, "Could not find the reparation item for the repair item id " + repairedInvoiceItem.getId() + " - this should never happen!");
+ }
+ }
+
+ // Filter the invoice items for analytics
+ final Collection<InvoiceItem> invoiceItemsForAnalytics = new LinkedList<InvoiceItem>();
+ for (final InvoiceItem invoiceItem : allInvoiceItems) {
+ if (InvoiceItemType.REPAIR_ADJ.equals(invoiceItem.getInvoiceItemType())) {
+ // We don't care, we'll create a special item for it below
+ } else if (reparationInvoiceItemIdToRepairItemMappings.keySet().contains(invoiceItem.getId())) {
+ // We do care - this is a reparation item. Create an item adjustment for it
+ final InvoiceItem repairInvoiceItem = reparationInvoiceItemIdToRepairItemMappings.get(invoiceItem.getId());
+ final InvoiceItem reparationInvoiceItem = invoiceItem;
+ invoiceItemsForAnalytics.add(new AdjustmentInvoiceItemForRepair(repairInvoiceItem, reparationInvoiceItem));
+ } else {
+ invoiceItemsForAnalytics.add(invoiceItem);
+ }
+ }
+
+ return invoiceItemsForAnalytics;
+ }
+
+ private class AdjustmentInvoiceItemForRepair implements InvoiceItem {
+
+ private final InvoiceItem repairInvoiceItem;
+ private final InvoiceItem reparationInvoiceItem;
+
+ private AdjustmentInvoiceItemForRepair(final InvoiceItem repairInvoiceItem,
+ final InvoiceItem reparationInvoiceItem) {
+ this.repairInvoiceItem = repairInvoiceItem;
+ this.reparationInvoiceItem = reparationInvoiceItem;
+ }
+
+ @Override
+ public InvoiceItemType getInvoiceItemType() {
+ return InvoiceItemType.ITEM_ADJ;
+ }
+
+ @Override
+ public UUID getInvoiceId() {
+ return repairInvoiceItem.getInvoiceId();
+ }
+
+ @Override
+ public UUID getAccountId() {
+ return repairInvoiceItem.getAccountId();
+ }
+
+ @Override
+ public LocalDate getStartDate() {
+ return repairInvoiceItem.getStartDate();
+ }
+
+ @Override
+ public LocalDate getEndDate() {
+ return repairInvoiceItem.getStartDate();
+ }
+
+ @Override
+ public BigDecimal getAmount() {
+ return reparationInvoiceItem.getAmount().add(repairInvoiceItem.getAmount());
+ }
+
+ @Override
+ public Currency getCurrency() {
+ return repairInvoiceItem.getCurrency();
+ }
+
+ @Override
+ public String getDescription() {
+ return null;
+ }
+
+ @Override
+ public UUID getBundleId() {
+ return null;
+ }
+
+ @Override
+ public UUID getSubscriptionId() {
+ return null;
+ }
+
+ @Override
+ public String getPlanName() {
+ return null;
+ }
+
+ @Override
+ public String getPhaseName() {
+ return null;
+ }
+
+ @Override
+ public BigDecimal getRate() {
+ return null;
+ }
+
+ @Override
+ public UUID getLinkedItemId() {
+ return repairInvoiceItem.getLinkedItemId();
+ }
+
+ @Override
+ public int compareTo(final InvoiceItem o) {
+ return repairInvoiceItem.compareTo(o);
+ }
+
+ @Override
+ public UUID getId() {
+ // We pretend to be the repair, the reparation item record id
+ // will be available as secondId
+ return repairInvoiceItem.getId();
+ }
+
+ public UUID getSecondId() {
+ return reparationInvoiceItem.getId();
+ }
+
+ @Override
+ public DateTime getCreatedDate() {
+ return repairInvoiceItem.getCreatedDate();
+ }
+
+ @Override
+ public DateTime getUpdatedDate() {
+ return repairInvoiceItem.getUpdatedDate();
+ }
+ }
}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceAdjustmentModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceAdjustmentModelDao.java
index a437645..2114226 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceAdjustmentModelDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceAdjustmentModelDao.java
@@ -34,6 +34,7 @@ public class BusinessInvoiceAdjustmentModelDao extends BusinessInvoiceItemBaseMo
final Long accountRecordId,
final Invoice invoice,
final InvoiceItem invoiceItem,
+ final Boolean revenueRecognizable,
final Long invoiceItemRecordId,
final Long secondInvoiceItemRecordId,
@Nullable final SubscriptionBundle bundle,
@@ -46,6 +47,7 @@ public class BusinessInvoiceAdjustmentModelDao extends BusinessInvoiceItemBaseMo
accountRecordId,
invoice,
invoiceItem,
+ revenueRecognizable,
invoiceItemRecordId,
secondInvoiceItemRecordId,
bundle,
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemAdjustmentModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemAdjustmentModelDao.java
index fab5ff4..bbc2af6 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemAdjustmentModelDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemAdjustmentModelDao.java
@@ -34,6 +34,7 @@ public class BusinessInvoiceItemAdjustmentModelDao extends BusinessInvoiceItemBa
final Long accountRecordId,
final Invoice invoice,
final InvoiceItem invoiceItem,
+ final Boolean revenueRecognizable,
final Long invoiceItemRecordId,
final Long secondInvoiceItemRecordId,
@Nullable final SubscriptionBundle bundle,
@@ -46,6 +47,7 @@ public class BusinessInvoiceItemAdjustmentModelDao extends BusinessInvoiceItemBa
accountRecordId,
invoice,
invoiceItem,
+ revenueRecognizable,
invoiceItemRecordId,
secondInvoiceItemRecordId,
bundle,
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemBaseModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemBaseModelDao.java
index 4059810..b04fe8d 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemBaseModelDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemBaseModelDao.java
@@ -30,7 +30,6 @@ import com.ning.billing.catalog.api.PlanPhase;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceItem;
-import com.ning.billing.invoice.api.InvoiceItemType;
import com.ning.billing.util.audit.AuditLog;
public abstract class BusinessInvoiceItemBaseModelDao extends BusinessModelDaoBase {
@@ -71,10 +70,19 @@ public abstract class BusinessInvoiceItemBaseModelDao extends BusinessModelDaoBa
private String currency;
private UUID linkedItemId;
+ public enum BusinessInvoiceItemType {
+ INVOICE_ADJUSTMENT,
+ INVOICE_ITEM_ADJUSTMENT,
+ ACCOUNT_CREDIT,
+ CHARGE
+ }
+
public static BusinessInvoiceItemBaseModelDao create(final Account account,
final Long accountRecordId,
final Invoice invoice,
final InvoiceItem invoiceItem,
+ final Boolean revenueRecognizable,
+ final BusinessInvoiceItemType businessInvoiceItemType,
final Long invoiceItemRecordId,
final Long secondInvoiceItemRecordId,
@Nullable final SubscriptionBundle bundle,
@@ -83,11 +91,12 @@ public abstract class BusinessInvoiceItemBaseModelDao extends BusinessModelDaoBa
final AuditLog creationAuditLog,
final Long tenantRecordId,
@Nullable final ReportGroup reportGroup) {
- if (InvoiceItemType.REFUND_ADJ.equals(invoiceItem.getInvoiceItemType())) {
+ if (BusinessInvoiceItemType.INVOICE_ADJUSTMENT.equals(businessInvoiceItemType)) {
return new BusinessInvoiceAdjustmentModelDao(account,
accountRecordId,
invoice,
invoiceItem,
+ revenueRecognizable,
invoiceItemRecordId,
secondInvoiceItemRecordId,
bundle,
@@ -96,13 +105,12 @@ public abstract class BusinessInvoiceItemBaseModelDao extends BusinessModelDaoBa
creationAuditLog,
tenantRecordId,
reportGroup);
- } else if (InvoiceItemType.EXTERNAL_CHARGE.equals(invoiceItem.getInvoiceItemType()) ||
- InvoiceItemType.FIXED.equals(invoiceItem.getInvoiceItemType()) ||
- InvoiceItemType.RECURRING.equals(invoiceItem.getInvoiceItemType())) {
+ } else if (BusinessInvoiceItemType.CHARGE.equals(businessInvoiceItemType)) {
return new BusinessInvoiceItemModelDao(account,
accountRecordId,
invoice,
invoiceItem,
+ revenueRecognizable,
invoiceItemRecordId,
secondInvoiceItemRecordId,
bundle,
@@ -111,11 +119,12 @@ public abstract class BusinessInvoiceItemBaseModelDao extends BusinessModelDaoBa
creationAuditLog,
tenantRecordId,
reportGroup);
- } else if (InvoiceItemType.ITEM_ADJ.equals(invoiceItem.getInvoiceItemType())) {
+ } else if (BusinessInvoiceItemType.INVOICE_ADJUSTMENT.equals(businessInvoiceItemType)) {
return new BusinessInvoiceItemAdjustmentModelDao(account,
accountRecordId,
invoice,
invoiceItem,
+ revenueRecognizable,
invoiceItemRecordId,
secondInvoiceItemRecordId,
bundle,
@@ -124,12 +133,12 @@ public abstract class BusinessInvoiceItemBaseModelDao extends BusinessModelDaoBa
creationAuditLog,
tenantRecordId,
reportGroup);
- } else if (InvoiceItemType.CBA_ADJ.equals(invoiceItem.getInvoiceItemType()) ||
- InvoiceItemType.CREDIT_ADJ.equals(invoiceItem.getInvoiceItemType())) {
+ } else if (BusinessInvoiceItemType.ACCOUNT_CREDIT.equals(businessInvoiceItemType)) {
return new BusinessInvoiceItemCreditModelDao(account,
accountRecordId,
invoice,
invoiceItem,
+ revenueRecognizable,
invoiceItemRecordId,
secondInvoiceItemRecordId,
bundle,
@@ -228,6 +237,7 @@ public abstract class BusinessInvoiceItemBaseModelDao extends BusinessModelDaoBa
final Long accountRecordId,
final Invoice invoice,
final InvoiceItem invoiceItem,
+ final Boolean revenueRecognizable,
final Long invoiceItemRecordId,
final Long secondInvoiceItemRecordId,
@Nullable final SubscriptionBundle bundle,
@@ -251,7 +261,7 @@ public abstract class BusinessInvoiceItemBaseModelDao extends BusinessModelDaoBa
invoice.getOriginalChargedAmount(),
invoice.getCreditAdjAmount(),
invoiceItem.getInvoiceItemType().toString(),
- null /* TODO */,
+ revenueRecognizable,
bundle == null ? null : bundle.getExternalKey(),
plan != null ? plan.getProduct().getName() : null,
plan != null ? plan.getProduct().getCatalogName() : null,
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemCreditModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemCreditModelDao.java
index 1d64378..bf26129 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemCreditModelDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemCreditModelDao.java
@@ -34,6 +34,7 @@ public class BusinessInvoiceItemCreditModelDao extends BusinessInvoiceItemBaseMo
final Long accountRecordId,
final Invoice invoice,
final InvoiceItem invoiceItem,
+ final Boolean revenueRecognizable,
final Long invoiceItemRecordId,
final Long secondInvoiceItemRecordId,
@Nullable final SubscriptionBundle bundle,
@@ -46,6 +47,7 @@ public class BusinessInvoiceItemCreditModelDao extends BusinessInvoiceItemBaseMo
accountRecordId,
invoice,
invoiceItem,
+ revenueRecognizable,
invoiceItemRecordId,
secondInvoiceItemRecordId,
bundle,
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemModelDao.java
index 3558946..1e5602a 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemModelDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemModelDao.java
@@ -34,6 +34,7 @@ public class BusinessInvoiceItemModelDao extends BusinessInvoiceItemBaseModelDao
final Long accountRecordId,
final Invoice invoice,
final InvoiceItem invoiceItem,
+ final Boolean revenueRecognizable,
final Long invoiceItemRecordId,
final Long secondInvoiceItemRecordId,
@Nullable final SubscriptionBundle bundle,
@@ -46,6 +47,7 @@ public class BusinessInvoiceItemModelDao extends BusinessInvoiceItemBaseModelDao
accountRecordId,
invoice,
invoiceItem,
+ revenueRecognizable,
invoiceItemRecordId,
secondInvoiceItemRecordId,
bundle,
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessModelDaoBase.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessModelDaoBase.java
index a36b589..8b64215 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessModelDaoBase.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessModelDaoBase.java
@@ -148,9 +148,6 @@ public abstract class BusinessModelDaoBase {
final BusinessModelDaoBase that = (BusinessModelDaoBase) o;
- if (DEFAULT_REPORT_GROUP != null ? !DEFAULT_REPORT_GROUP.equals(that.DEFAULT_REPORT_GROUP) : that.DEFAULT_REPORT_GROUP != null) {
- return false;
- }
if (accountExternalKey != null ? !accountExternalKey.equals(that.accountExternalKey) : that.accountExternalKey != null) {
return false;
}
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/AnalyticsTestSuiteNoDB.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/AnalyticsTestSuiteNoDB.java
index 97c0729..fd1d2a3 100644
--- a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/AnalyticsTestSuiteNoDB.java
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/AnalyticsTestSuiteNoDB.java
@@ -23,6 +23,8 @@ import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import org.mockito.Mockito;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
@@ -49,6 +51,7 @@ import com.ning.billing.junction.api.Blockable.Type;
import com.ning.billing.junction.api.BlockingState;
import com.ning.billing.osgi.bundles.analytics.api.BusinessEntityBase;
import com.ning.billing.osgi.bundles.analytics.dao.TestCallContext;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceItemBaseModelDao.BusinessInvoiceItemType;
import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessModelDaoBase;
import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessModelDaoBase.ReportGroup;
import com.ning.billing.payment.api.Payment;
@@ -72,6 +75,8 @@ import com.google.common.collect.ImmutableList;
public abstract class AnalyticsTestSuiteNoDB {
+ protected final Logger logger = LoggerFactory.getLogger(AnalyticsTestSuiteNoDB.class);
+
protected final Long accountRecordId = 1L;
protected final Long subscriptionEventRecordId = 2L;
protected final Long invoiceRecordId = 3L;
@@ -84,6 +89,7 @@ public abstract class AnalyticsTestSuiteNoDB {
protected final Long tenantRecordId = 9L;
protected final ReportGroup reportGroup = ReportGroup.partner;
+ protected final BusinessInvoiceItemType invoiceItemType = BusinessInvoiceItemType.INVOICE_ITEM_ADJUSTMENT;
protected Account account;
protected SubscriptionBundle bundle;
@@ -94,6 +100,7 @@ public abstract class AnalyticsTestSuiteNoDB {
protected BlockingState blockingState;
protected Invoice invoice;
protected InvoiceItem invoiceItem;
+ protected Boolean recognizable;
protected InvoicePayment invoicePayment;
protected PaymentAttempt paymentAttempt;
protected PaymentMethod paymentMethod;
@@ -239,6 +246,8 @@ public abstract class AnalyticsTestSuiteNoDB {
Mockito.when(invoiceItem.getCreatedDate()).thenReturn(new DateTime(2016, 1, 22, 10, 56, 51, DateTimeZone.UTC));
final UUID invoiceItemId = invoiceItem.getId();
+ recognizable = false;
+
final UUID invoiceId = UUID.randomUUID();
invoicePayment = Mockito.mock(InvoicePayment.class);
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessInvoice.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessInvoice.java
index f59b325..51fdbb1 100644
--- a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessInvoice.java
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessInvoice.java
@@ -40,6 +40,8 @@ public class TestBusinessInvoice extends AnalyticsTestSuiteNoDB {
accountRecordId,
invoice,
invoiceItem,
+ recognizable,
+ invoiceItemType,
invoiceItemRecordId,
secondInvoiceItemRecordId,
bundle,
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessInvoiceItem.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessInvoiceItem.java
index bd857d5..c7c2c55 100644
--- a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessInvoiceItem.java
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessInvoiceItem.java
@@ -30,6 +30,8 @@ public class TestBusinessInvoiceItem extends AnalyticsTestSuiteNoDB {
accountRecordId,
invoice,
invoiceItem,
+ recognizable,
+ invoiceItemType,
invoiceItemRecordId,
secondInvoiceItemRecordId,
bundle,
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessSnapshot.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessSnapshot.java
index c6b4773..b741810 100644
--- a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessSnapshot.java
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessSnapshot.java
@@ -78,6 +78,8 @@ public class TestBusinessSnapshot extends AnalyticsTestSuiteNoDB {
accountRecordId,
invoice,
invoiceItem,
+ recognizable,
+ invoiceItemType,
invoiceItemRecordId,
secondInvoiceItemRecordId,
bundle,
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoiceItemModelDao.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoiceItemModelDao.java
index bab0f9c..1d867e8 100644
--- a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoiceItemModelDao.java
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoiceItemModelDao.java
@@ -29,6 +29,7 @@ public class TestBusinessInvoiceItemModelDao extends AnalyticsTestSuiteNoDB {
accountRecordId,
invoice,
invoiceItem,
+ recognizable,
invoiceItemRecordId,
secondInvoiceItemRecordId,
null,
@@ -53,6 +54,7 @@ public class TestBusinessInvoiceItemModelDao extends AnalyticsTestSuiteNoDB {
accountRecordId,
invoice,
invoiceItem,
+ recognizable,
invoiceItemRecordId,
secondInvoiceItemRecordId,
bundle,
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessInvoiceDao.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessInvoiceDao.java
index 5a50590..9576342 100644
--- a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessInvoiceDao.java
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessInvoiceDao.java
@@ -1,4 +1,200 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.osgi.bundles.analytics.dao;
-public class TestBusinessInvoiceDao {
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+import javax.sql.DataSource;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
+import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteNoDB;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillDataSource;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillLogService;
+
+import com.google.common.collect.ImmutableList;
+
+public class TestBusinessInvoiceDao extends AnalyticsTestSuiteNoDB {
+
+ private final UUID accountId = UUID.randomUUID();
+ private final UUID invoiceId = UUID.randomUUID();
+ private final UUID bundleId = UUID.randomUUID();
+
+ private BusinessInvoiceDao invoiceDao;
+
+ @Override
+ @BeforeMethod
+ public void setUp() throws Exception {
+ super.setUp();
+
+ final OSGIKillbillDataSource osgiKillbillDataSource = Mockito.mock(OSGIKillbillDataSource.class);
+
+ final DataSource dataSource = Mockito.mock(DataSource.class);
+ Mockito.when(osgiKillbillDataSource.getDataSource()).thenReturn(dataSource);
+
+ final OSGIKillbillLogService osgiKillbillLogService = Mockito.mock(OSGIKillbillLogService.class);
+ Mockito.doAnswer(new Answer() {
+ @Override
+ public Object answer(final InvocationOnMock invocation) throws Throwable {
+ logger.info(Arrays.toString(invocation.getArguments()));
+ return null;
+ }
+ }).when(osgiKillbillLogService).log(Mockito.anyInt(), Mockito.anyString());
+
+ invoiceDao = new BusinessInvoiceDao(osgiKillbillLogService, null, osgiKillbillDataSource, null);
+ }
+
+ @Test(groups = "fast")
+ public void testRevenueRecognizable() throws Exception {
+ // All items but CREDIT_ADJ are recognizable by default
+ Assert.assertTrue(invoiceDao.isRevenueRecognizable(createInvoiceItem(InvoiceItemType.RECURRING)
+ ));
+ Assert.assertFalse(invoiceDao.isRevenueRecognizable(createInvoiceItem(InvoiceItemType.CREDIT_ADJ)
+ ));
+ }
+
+ @Test(groups = "fast")
+ public void testInvoiceAdjustment() throws Exception {
+ Assert.assertFalse(invoiceDao.isInvoiceAdjustmentItem(createInvoiceItem(InvoiceItemType.RECURRING),
+ ImmutableList.<InvoiceItem>of()));
+ Assert.assertTrue(invoiceDao.isInvoiceAdjustmentItem(createInvoiceItem(InvoiceItemType.REFUND_ADJ),
+ ImmutableList.<InvoiceItem>of()));
+
+ final InvoiceItem creditAdj = createInvoiceItem(InvoiceItemType.CREDIT_ADJ);
+
+ // Account credit
+ Assert.assertFalse(invoiceDao.isInvoiceAdjustmentItem(creditAdj,
+ ImmutableList.<InvoiceItem>of(createInvoiceItem(InvoiceItemType.CBA_ADJ, creditAdj.getAmount().negate()))));
+
+ Assert.assertTrue(invoiceDao.isInvoiceAdjustmentItem(creditAdj,
+ ImmutableList.<InvoiceItem>of(createInvoiceItem(InvoiceItemType.CBA_ADJ, creditAdj.getAmount().negate().add(BigDecimal.ONE)))));
+ Assert.assertTrue(invoiceDao.isInvoiceAdjustmentItem(creditAdj,
+ ImmutableList.<InvoiceItem>of(createInvoiceItem(InvoiceItemType.RECURRING),
+ createInvoiceItem(InvoiceItemType.CBA_ADJ, creditAdj.getAmount().negate()))));
+ }
+
+ @Test(groups = "fast")
+ public void testSanitization() throws Exception {
+ // One invoice, with two repairs and an external charge
+ final UUID subscriptionId1 = UUID.randomUUID();
+ final LocalDate startDate1 = new LocalDate(2013, 4, 1);
+ final LocalDate endDate1 = new LocalDate(2013, 4, 30);
+ final BigDecimal amount1 = new BigDecimal("30");
+ final InvoiceItem recurring1 = createInvoiceItem(InvoiceItemType.RECURRING, subscriptionId1, startDate1, endDate1, amount1, null);
+ final InvoiceItem repair1 = createInvoiceItem(InvoiceItemType.REPAIR_ADJ, subscriptionId1, startDate1, endDate1, amount1.negate(), recurring1.getId());
+ final LocalDate reparationEndDate1 = new LocalDate(2013, 4, 10);
+ final BigDecimal reparationAmount1 = new BigDecimal("10");
+ final InvoiceItem reparation1 = createInvoiceItem(InvoiceItemType.RECURRING, subscriptionId1, startDate1, reparationEndDate1, reparationAmount1, null);
+
+ final UUID subscriptionId2 = UUID.randomUUID();
+ final LocalDate startDate2 = new LocalDate(2013, 4, 10);
+ final LocalDate endDate2 = new LocalDate(2013, 4, 30);
+ final BigDecimal amount2 = new BigDecimal("20");
+ final InvoiceItem recurring2 = createInvoiceItem(InvoiceItemType.RECURRING, subscriptionId2, startDate2, endDate2, amount2, null);
+ final InvoiceItem repair2 = createInvoiceItem(InvoiceItemType.REPAIR_ADJ, subscriptionId2, startDate2, endDate2, amount2.negate(), recurring2.getId());
+ final LocalDate reparationEndDate2 = new LocalDate(2013, 4, 15);
+ final BigDecimal reparationAmount2 = new BigDecimal("5");
+ final InvoiceItem reparation2 = createInvoiceItem(InvoiceItemType.RECURRING, subscriptionId2, startDate2, reparationEndDate2, reparationAmount2, null);
+
+ final UUID externalChargeSubscriptionId = UUID.randomUUID();
+ final LocalDate externalStartDate = new LocalDate(2012, 1, 1);
+ final BigDecimal externalChargeAmount = BigDecimal.TEN;
+ final InvoiceItem externalCharge = createInvoiceItem(InvoiceItemType.EXTERNAL_CHARGE, externalChargeSubscriptionId, externalStartDate, null, externalChargeAmount, null);
+
+ final Collection<InvoiceItem> sanitizedInvoiceItems = invoiceDao.sanitizeInvoiceItems(ImmutableList.<InvoiceItem>of(recurring1, repair1, reparation1, recurring2, repair2, reparation2, externalCharge));
+ Assert.assertEquals(sanitizedInvoiceItems.size(), 2 + 2 + 1);
+ for (final InvoiceItem invoiceItem : sanitizedInvoiceItems) {
+ if (invoiceItem.getId().equals(recurring1.getId())) {
+ Assert.assertEquals(invoiceItem, recurring1);
+ } else if (invoiceItem.getId().equals(repair1.getId())) {
+ if (InvoiceItemType.ITEM_ADJ.equals(invoiceItem.getInvoiceItemType()) && invoiceItem.getLinkedItemId().equals(recurring1.getId())) {
+ Assert.assertEquals(invoiceItem.getAmount(), new BigDecimal("20").negate());
+ } else {
+ Assert.fail("Repair item 1 shouldn't be in the sanitized elements");
+ }
+ } else if (invoiceItem.getId().equals(reparation1.getId())) {
+ Assert.fail("Reparation item 1 shouldn't be in the sanitized elements");
+ } else if (invoiceItem.getId().equals(recurring2.getId())) {
+ Assert.assertEquals(invoiceItem, recurring2);
+ } else if (invoiceItem.getId().equals(repair2.getId())) {
+ if (InvoiceItemType.ITEM_ADJ.equals(invoiceItem.getInvoiceItemType()) && invoiceItem.getLinkedItemId().equals(recurring2.getId())) {
+ Assert.assertEquals(invoiceItem.getAmount(), new BigDecimal("15").negate());
+ } else {
+ Assert.fail("Repair item 2 shouldn't be in the sanitized elements");
+ }
+ } else if (invoiceItem.getId().equals(reparation2.getId())) {
+ Assert.fail("Reparation item 2 shouldn't be in the sanitized elements");
+ } else if (invoiceItem.getId().equals(externalCharge.getId())) {
+ Assert.assertEquals(invoiceItem, externalCharge);
+ } else {
+ Assert.fail("Shouldn't be in the sanitized elements: " + invoiceItem);
+ }
+ }
+ }
+
+ private InvoiceItem createInvoiceItem(final InvoiceItemType type) {
+ return createInvoiceItem(type, BigDecimal.TEN);
+ }
+
+ private InvoiceItem createInvoiceItem(final InvoiceItemType type, final BigDecimal amount) {
+ return createInvoiceItem(type, UUID.randomUUID(), new LocalDate(2013, 1, 2), new LocalDate(2013, 2, 5), amount, null);
+ }
+
+ private InvoiceItem createInvoiceItem(final InvoiceItemType invoiceItemType,
+ final UUID subscriptionId,
+ final LocalDate startDate,
+ final LocalDate endDate,
+ final BigDecimal amount,
+ @Nullable final UUID linkedItemId) {
+ final UUID invoiceItemId = UUID.randomUUID();
+
+ final InvoiceItem invoiceItem = Mockito.mock(InvoiceItem.class);
+ Mockito.when(invoiceItem.getId()).thenReturn(invoiceItemId);
+ Mockito.when(invoiceItem.getInvoiceItemType()).thenReturn(invoiceItemType);
+ Mockito.when(invoiceItem.getInvoiceId()).thenReturn(invoiceId);
+ Mockito.when(invoiceItem.getAccountId()).thenReturn(accountId);
+ Mockito.when(invoiceItem.getStartDate()).thenReturn(startDate);
+ Mockito.when(invoiceItem.getEndDate()).thenReturn(endDate);
+ Mockito.when(invoiceItem.getAmount()).thenReturn(amount);
+ Mockito.when(invoiceItem.getCurrency()).thenReturn(Currency.EUR);
+ Mockito.when(invoiceItem.getDescription()).thenReturn(UUID.randomUUID().toString());
+ Mockito.when(invoiceItem.getBundleId()).thenReturn(bundleId);
+ Mockito.when(invoiceItem.getSubscriptionId()).thenReturn(subscriptionId);
+ Mockito.when(invoiceItem.getPlanName()).thenReturn(UUID.randomUUID().toString());
+ Mockito.when(invoiceItem.getPhaseName()).thenReturn(UUID.randomUUID().toString());
+ Mockito.when(invoiceItem.getRate()).thenReturn(new BigDecimal("1203"));
+ Mockito.when(invoiceItem.getLinkedItemId()).thenReturn(linkedItemId);
+ Mockito.when(invoiceItem.getCreatedDate()).thenReturn(new DateTime(2016, 1, 22, 10, 56, 51, DateTimeZone.UTC));
+
+ return invoiceItem;
+ }
}