killbill-aplcache
Changes
invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatter.java 15(+13 -2)
Details
diff --git a/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java b/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
index 923591c..a340b6f 100644
--- a/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
+++ b/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
@@ -87,31 +87,9 @@ public class DefaultAccount extends ExtendedEntityBase implements Account {
updatedBy, updatedDate);
}
- /**
+ /*
* This call is used for testing and update from an existing account
- * @param id
- * @param externalKey
- * @param email
- * @param name
- * @param firstNameLength
- * @param currency
- * @param billCycleDay
- * @param paymentProviderName
- * @param timeZone
- * @param locale
- * @param address1
- * @param address2
- * @param companyName
- * @param city
- * @param stateOrProvince
- * @param country
- * @param postalCode
- * @param phone
- * @param isMigrated
- * @param isNotifiedForInvoices
- * @param createdDate
- * @param updatedDate
- */
+ */
public DefaultAccount(final UUID id, final String externalKey, final String email,
final String name, final int firstNameLength,
final Currency currency, final int billCycleDay, final String paymentProviderName,
@@ -296,7 +274,7 @@ public class DefaultAccount extends ExtendedEntityBase implements Account {
", stateOrProvince=" + stateOrProvince +
", postalCode=" + postalCode +
", country=" + country +
- ", tags=" + tags +
+ ", tagStore=" + tagStore +
", fields=" + fields +
"]";
}
diff --git a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
index 6c7e78f..8f09d7d 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
@@ -32,7 +32,7 @@ import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.callcontext.CallContextFactory;
import com.ning.billing.util.customfield.CustomField;
import com.ning.billing.util.entity.EntityPersistenceException;
-import com.ning.billing.util.tag.Tag;
+import com.ning.billing.util.tag.TagDefinition;
import org.joda.time.DateTime;
public class DefaultAccountUserApi implements com.ning.billing.account.api.AccountUserApi {
@@ -47,10 +47,10 @@ public class DefaultAccountUserApi implements com.ning.billing.account.api.Accou
@Override
public Account createAccount(final AccountData data, final List<CustomField> fields,
- final List<Tag> tags, final CallContext context) throws AccountApiException {
+ final List<TagDefinition> tagDefinitions, final CallContext context) throws AccountApiException {
Account account = new DefaultAccount(data);
account.setFields(fields);
- account.addTags(tags);
+ account.addTagsFromDefinitions(tagDefinitions);
try {
dao.create(account, context);
@@ -113,14 +113,14 @@ public class DefaultAccountUserApi implements com.ning.billing.account.api.Accou
@Override
public Account migrateAccount(final MigrationAccountData data, final List<CustomField> fields,
- final List<Tag> tags, final CallContext context)
+ final List<TagDefinition> tagDefinitions, final CallContext context)
throws AccountApiException {
DateTime createdDate = data.getCreatedDate() == null ? context.getCreatedDate() : data.getCreatedDate();
DateTime updatedDate = data.getUpdatedDate() == null ? context.getUpdatedDate() : data.getUpdatedDate();
CallContext migrationContext = factory.toMigrationCallContext(context, createdDate, updatedDate);
Account account = new DefaultAccount(data);
account.setFields(fields);
- account.addTags(tags);
+ account.addTagsFromDefinitions(tagDefinitions);
try {
dao.create(account, migrationContext);
diff --git a/account/src/test/java/com/ning/billing/account/api/MockAccountUserApi.java b/account/src/test/java/com/ning/billing/account/api/MockAccountUserApi.java
index 8e51da0..9396541 100644
--- a/account/src/test/java/com/ning/billing/account/api/MockAccountUserApi.java
+++ b/account/src/test/java/com/ning/billing/account/api/MockAccountUserApi.java
@@ -22,11 +22,11 @@ import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.tag.TagDefinition;
import org.joda.time.DateTimeZone;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.util.customfield.CustomField;
-import com.ning.billing.util.tag.Tag;
public class MockAccountUserApi implements AccountUserApi {
private final CopyOnWriteArrayList<Account> accounts = new CopyOnWriteArrayList<Account>();
@@ -61,7 +61,7 @@ public class MockAccountUserApi implements AccountUserApi {
@Override
public Account createAccount(final AccountData data, final List<CustomField> fields,
- final List<Tag> tags, final CallContext context) throws AccountApiException {
+ final List<TagDefinition> tagDefinitions, final CallContext context) throws AccountApiException {
Account result = new DefaultAccount(data);
accounts.add(result);
return result;
@@ -119,7 +119,7 @@ public class MockAccountUserApi implements AccountUserApi {
@Override
public Account migrateAccount(final MigrationAccountData data,
- final List<CustomField> fields, final List<Tag> tags, final CallContext context)
+ final List<CustomField> fields, final List<TagDefinition> tagDefinitions, final CallContext context)
throws AccountApiException {
Account result = new DefaultAccount(data);
accounts.add(result);
diff --git a/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java b/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
index 3a51bcc..66e49bb 100644
--- a/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
+++ b/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
@@ -67,7 +67,7 @@ public abstract class AccountDaoTestBase {
accountDao.test();
Clock clock = injector.getInstance(Clock.class);
- context = new DefaultCallContextFactory(clock).createCallContext("Vizzini", CallOrigin.TEST, UserType.TEST);
+ context = new DefaultCallContextFactory(clock).createCallContext("Account Dao Tests", CallOrigin.TEST, UserType.TEST);
BusService busService = injector.getInstance(BusService.class);
diff --git a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
index 71cbeb1..20f372e 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
@@ -26,6 +26,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.UUID;
+import com.ning.billing.util.tag.TagDefinition;
import org.apache.commons.io.IOUtils;
import org.joda.time.DateTime;
import org.testng.Assert;
@@ -87,8 +88,6 @@ import com.ning.billing.util.callcontext.UserType;
import com.ning.billing.util.clock.Clock;
import com.ning.billing.util.clock.DefaultClock;
import com.ning.billing.util.tag.DefaultTagDefinition;
-import com.ning.billing.util.tag.DescriptiveTag;
-import com.ning.billing.util.tag.Tag;
import com.ning.billing.util.tag.dao.TagDefinitionSqlDao;
@Guice(modules = {AnalyticsTestModule.class, MockCatalogModule.class})
@@ -161,11 +160,11 @@ public class TestAnalyticsService {
final MockAccount account = new MockAccount(UUID.randomUUID(), ACCOUNT_KEY, ACCOUNT_CURRENCY);
try {
- final List<Tag> tags = new ArrayList<Tag>();
- tags.add(new DescriptiveTag(TAG_ONE));
- tags.add(new DescriptiveTag(TAG_TWO));
+ final List<TagDefinition> tagDefinitions = new ArrayList<TagDefinition>();
+ tagDefinitions.add(TAG_ONE);
+ tagDefinitions.add(TAG_TWO);
- final Account storedAccount = accountApi.createAccount(account, null, tags, context);
+ final Account storedAccount = accountApi.createAccount(account, null, tagDefinitions, context);
// Create events for the bus and expected results
createSubscriptionTransitionEvent(storedAccount);
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java b/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java
index bd95758..159c707 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java
@@ -19,6 +19,7 @@ package com.ning.billing.analytics;
import java.util.List;
import java.util.UUID;
+import com.ning.billing.util.tag.ControlTagType;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
@@ -210,7 +211,12 @@ public class MockAccount implements Account
}
@Override
- public boolean hasTag(String tagName) {
+ public boolean hasTag(TagDefinition tagDefinition) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasTag(ControlTagType controlTagType) {
throw new UnsupportedOperationException();
}
@@ -225,6 +231,11 @@ public class MockAccount implements Account
}
@Override
+ public void addTagsFromDefinitions(List<TagDefinition> tagDefinitions) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void clearTags() {
throw new UnsupportedOperationException();
}
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockAccountUserApi.java b/analytics/src/test/java/com/ning/billing/analytics/MockAccountUserApi.java
index f015561..795360f 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockAccountUserApi.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockAccountUserApi.java
@@ -20,6 +20,7 @@ import java.util.List;
import java.util.UUID;
import com.ning.billing.account.api.AccountEmail;
+import com.ning.billing.util.tag.TagDefinition;
import org.apache.commons.lang.NotImplementedException;
import com.ning.billing.account.api.Account;
@@ -31,7 +32,6 @@ import com.ning.billing.account.api.MigrationAccountData;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.customfield.CustomField;
-import com.ning.billing.util.tag.Tag;
public class MockAccountUserApi implements AccountUserApi
{
@@ -45,7 +45,8 @@ public class MockAccountUserApi implements AccountUserApi
}
@Override
- public Account createAccount(final AccountData data, final List<CustomField> fields, final List<Tag> tags, final CallContext context)
+ public Account createAccount(final AccountData data, final List<CustomField> fields,
+ final List<TagDefinition> tagDefinitions, final CallContext context)
{
throw new UnsupportedOperationException();
}
@@ -89,7 +90,7 @@ public class MockAccountUserApi implements AccountUserApi
@Override
public Account migrateAccount(MigrationAccountData data,
- List<CustomField> fields, List<Tag> tags, final CallContext context)
+ List<CustomField> fields, List<TagDefinition> tagDefinitions, final CallContext context)
throws AccountApiException {
throw new UnsupportedOperationException();
}
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java b/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
index 7144edb..8937063 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
@@ -19,6 +19,7 @@ package com.ning.billing.analytics;
import java.util.List;
import java.util.UUID;
+import com.ning.billing.util.tag.ControlTagType;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
@@ -36,7 +37,6 @@ import com.ning.billing.util.customfield.CustomField;
import com.ning.billing.util.tag.Tag;
import com.ning.billing.util.tag.TagDefinition;
-
public class MockSubscription implements Subscription
{
private static final UUID ID = UUID.randomUUID();
@@ -212,7 +212,12 @@ public class MockSubscription implements Subscription
}
@Override
- public boolean hasTag(String tagName) {
+ public boolean hasTag(TagDefinition tagDefinition) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasTag(ControlTagType controlTagType) {
throw new UnsupportedOperationException();
}
@@ -227,6 +232,11 @@ public class MockSubscription implements Subscription
}
@Override
+ public void addTagsFromDefinitions(List<TagDefinition> tagDefinitions) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void clearTags() {
throw new UnsupportedOperationException();
}
diff --git a/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java b/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java
index 5ea43d0..9609598 100644
--- a/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java
+++ b/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java
@@ -21,22 +21,23 @@ import java.util.UUID;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.customfield.CustomField;
-import com.ning.billing.util.tag.Tag;
+import com.ning.billing.util.tag.TagDefinition;
import javax.annotation.Nullable;
public interface AccountUserApi {
public Account createAccount(AccountData data, @Nullable List<CustomField> fields,
- @Nullable List<Tag> tags, CallContext context) throws AccountApiException;
+ @Nullable List<TagDefinition> tagDefinitions, CallContext context) throws AccountApiException;
public Account migrateAccount(MigrationAccountData data, @Nullable List<CustomField> fields,
- @Nullable List<Tag> tags, CallContext context) throws AccountApiException;
+ @Nullable List<TagDefinition> tagDefinitions, CallContext context) throws AccountApiException;
/***
*
* Note: does not update the external key
- * @param account
- * @throws AccountApiException
+ * @param account account to be updated
+ * @param context contains specific information about the call
+ * @throws AccountApiException if a failure occurs
*/
public void updateAccount(Account account, CallContext context) throws AccountApiException;
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoiceItem.java b/api/src/main/java/com/ning/billing/invoice/api/InvoiceItem.java
index b7ab8b1..cd7decd 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoiceItem.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoiceItem.java
@@ -46,5 +46,5 @@ public interface InvoiceItem extends Entity, Comparable<InvoiceItem> {
Currency getCurrency();
- InvoiceItem asCredit();
+ InvoiceItem asReversingItem();
}
diff --git a/api/src/main/java/com/ning/billing/util/api/TagUserApi.java b/api/src/main/java/com/ning/billing/util/api/TagUserApi.java
index d5cb8f2..5d05bb0 100644
--- a/api/src/main/java/com/ning/billing/util/api/TagUserApi.java
+++ b/api/src/main/java/com/ning/billing/util/api/TagUserApi.java
@@ -57,7 +57,6 @@ public interface TagUserApi {
*/
public void deleteTagDefinition(String definitionName, CallContext context) throws TagDefinitionApiException;
-
/**
*
* @param name
diff --git a/api/src/main/java/com/ning/billing/util/tag/Taggable.java b/api/src/main/java/com/ning/billing/util/tag/Taggable.java
index 3b2e8b0..bb8aa1d 100644
--- a/api/src/main/java/com/ning/billing/util/tag/Taggable.java
+++ b/api/src/main/java/com/ning/billing/util/tag/Taggable.java
@@ -21,11 +21,16 @@ import org.joda.time.DateTime;
public interface Taggable {
public List<Tag> getTagList();
- public boolean hasTag(String tagName);
+
+ public boolean hasTag(TagDefinition definition);
+ public boolean hasTag(ControlTagType controlTagType);
+
public void addTag(TagDefinition definition);
public void addTags(List<Tag> tags);
+ public void addTagsFromDefinitions(List<TagDefinition> tagDefinitions);
public void clearTags();
public void removeTag(TagDefinition definition);
+
public boolean generateInvoice();
public boolean processPayment();
}
diff --git a/api/src/main/java/com/ning/billing/util/tag/TagStore.java b/api/src/main/java/com/ning/billing/util/tag/TagStore.java
index 705e203..22749b9 100644
--- a/api/src/main/java/com/ning/billing/util/tag/TagStore.java
+++ b/api/src/main/java/com/ning/billing/util/tag/TagStore.java
@@ -23,7 +23,9 @@ public interface TagStore extends EntityCollection<Tag> {
public boolean generateInvoice();
- public void remove(String tagName);
+ public boolean containsTagForDefinition(TagDefinition definition);
- public boolean containsTag(String tagName);
+ public boolean containsTagForControlTagType(ControlTagType controlTagType);
+
+ public Tag remove(TagDefinition tagDefinition);
}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
index 8da1bb8..71a23d1 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
@@ -49,8 +49,6 @@ import com.ning.billing.entitlement.api.user.SubscriptionData;
@Test(groups = "slow")
@Guice(modules = {MockModule.class})
public class TestIntegration extends TestIntegrationBase {
-
-
@Test(groups = "slow", enabled = true)
public void testBasePlanCompleteWithBillingDayInPast() throws Exception {
DateTime startDate = new DateTime(2012, 2, 1, 0, 3, 42, 0);
@@ -99,8 +97,8 @@ public class TestIntegration extends TestIntegrationBase {
}
}
- @Test(groups = "slow", enabled = true)
- public void testRepairChangeBPWithAddonIncluded() throws Exception {
+ @Test(groups = "slow", enabled = true)
+ public void testRepairChangeBPWithAddonIncluded() throws Exception {
DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
@@ -150,9 +148,39 @@ public class TestIntegration extends TestIntegrationBase {
busHandler.pushExpectedEvent(NextEvent.PAYMENT);
clock.addDeltaFromReality(it.toDurationMillis());
assertTrue(busHandler.isCompleted(DELAY));
- }
-
+ }
+ @Test(groups = {"slow"})
+ public void testRepairForInvoicing() throws AccountApiException, EntitlementUserApiException {
+ log.info("Starting testRepairForInvoicing");
+
+ Account account = accountUserApi.createAccount(getAccountData(1), null, null, context);
+ UUID accountId = account.getId();
+ assertNotNull(account);
+
+ DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+ clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+ SubscriptionBundle bundle = entitlementUserApi.createBundleForAccount(account.getId(), "someBundle", context);
+ assertNotNull(bundle);
+
+ String productName = "Shotgun";
+ BillingPeriod term = BillingPeriod.MONTHLY;
+ String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+ entitlementUserApi.createSubscription(bundle.getId(),
+ new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null), null, context);
+
+ busHandler.reset();
+ busHandler.pushExpectedEvent(NextEvent.CREATE);
+ busHandler.pushExpectedEvent(NextEvent.INVOICE);
+ assertTrue(busHandler.isCompleted(DELAY));
+
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId);
+ assertEquals(invoices.size(), 1);
+
+ // TODO: Jeff implement repair
+ }
+
@Test(groups = "slow", enabled = false)
public void testWithRecreatePlan() throws Exception {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
index d4e1685..e18b73d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
@@ -24,6 +24,7 @@ import java.util.UUID;
import javax.annotation.Nullable;
+import com.ning.billing.util.tag.ControlTagType;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,8 +55,7 @@ import com.ning.billing.util.clock.Clock;
import com.ning.billing.util.customfield.CustomField;
import com.ning.billing.util.entity.ExtendedEntityBase;
-public class SubscriptionData extends ExtendedEntityBase implements
- Subscription {
+public class SubscriptionData extends ExtendedEntityBase implements Subscription {
private final static Logger log = LoggerFactory.getLogger(SubscriptionData.class);
@@ -504,5 +504,4 @@ public class SubscriptionData extends ExtendedEntityBase implements
previousPriceList = nextPriceList;
}
}
-
}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
index e9bfe5c..a1241f6 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
@@ -86,7 +86,7 @@ public class TestUserApiAddOn extends TestApiBase {
}
@Test(enabled=true, groups={"slow"})
- public void testCancelBPWthAddon() {
+ public void testCancelBPWithAddon() {
try {
String baseProduct = "Shotgun";
@@ -114,6 +114,7 @@ public class TestUserApiAddOn extends TestApiBase {
// SET CTD TO CANCEL IN FUTURE
DateTime now = clock.getUTCNow();
Duration ctd = getDurationMonth(1);
+ // Why not just use clock.getUTCNow().plusMonths(1) ?
DateTime newChargedThroughDate = DefaultClock.addDuration(now, ctd);
billingApi.setChargedThroughDate(baseSubscription.getId(), newChargedThroughDate, context);
baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
@@ -126,7 +127,6 @@ public class TestUserApiAddOn extends TestApiBase {
assertEquals(aoSubscription.getState(), SubscriptionState.ACTIVE);
assertTrue(aoSubscription.isSubscriptionFutureCancelled());
-
// MOVE AFTER CANCELLATION
testListener.reset();
testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
@@ -145,7 +145,7 @@ public class TestUserApiAddOn extends TestApiBase {
@Test(enabled=true, groups={"slow"})
- public void testChangeBPWthAddonNonIncluded() {
+ public void testChangeBPWithAddonNonIncluded() {
try {
String baseProduct = "Shotgun";
@@ -198,7 +198,7 @@ public class TestUserApiAddOn extends TestApiBase {
}
@Test(enabled=true, groups={"slow"})
- public void testChangeBPWthAddonNonAvailable() {
+ public void testChangeBPWithAddonNonAvailable() {
try {
String baseProduct = "Shotgun";
diff --git a/invoice/src/main/java/com/ning/billing/invoice/glue/InvoiceModule.java b/invoice/src/main/java/com/ning/billing/invoice/glue/InvoiceModule.java
index 8fd0386..c5350b8 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/glue/InvoiceModule.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/glue/InvoiceModule.java
@@ -17,8 +17,12 @@
package com.ning.billing.invoice.glue;
import com.ning.billing.invoice.api.InvoiceNotifier;
+import com.ning.billing.invoice.api.formatters.InvoiceFormatterFactory;
import com.ning.billing.invoice.notification.EmailInvoiceNotifier;
-import com.ning.billing.util.email.EmailConfig;
+import com.ning.billing.invoice.template.formatters.DefaultInvoiceFormatterFactory;
+import com.ning.billing.util.email.templates.MustacheTemplateEngine;
+import com.ning.billing.util.email.templates.TemplateEngine;
+import com.ning.billing.util.template.translation.TranslatorConfig;
import org.skife.config.ConfigurationObjectFactory;
import com.google.inject.AbstractModule;
@@ -71,6 +75,10 @@ public class InvoiceModule extends AbstractModule {
protected void installNotifiers() {
bind(NextBillingDateNotifier.class).to(DefaultNextBillingDateNotifier.class).asEagerSingleton();
bind(NextBillingDatePoster.class).to(DefaultNextBillingDatePoster.class).asEagerSingleton();
+ TranslatorConfig config = new ConfigurationObjectFactory(System.getProperties()).build(TranslatorConfig.class);
+ bind(TranslatorConfig.class).toInstance(config);
+ bind(InvoiceFormatterFactory.class).to(DefaultInvoiceFormatterFactory.class).asEagerSingleton();
+ bind(TemplateEngine.class).to(MustacheTemplateEngine.class).asEagerSingleton();
bind(InvoiceNotifier.class).to(EmailInvoiceNotifier.class).asEagerSingleton();
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/CreditInvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/CreditInvoiceItem.java
new file mode 100644
index 0000000..58c4a49
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/CreditInvoiceItem.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2010-2011 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.invoice.model;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.InvoiceItem;
+import org.joda.time.DateTime;
+
+import javax.annotation.Nullable;
+import java.math.BigDecimal;
+import java.util.UUID;
+
+public class CreditInvoiceItem extends InvoiceItemBase {
+ public CreditInvoiceItem(UUID invoiceId, UUID accountId, DateTime date, BigDecimal amount, Currency currency) {
+ this(UUID.randomUUID(), invoiceId, accountId, date, amount, currency, null, null);
+ }
+
+ public CreditInvoiceItem(UUID id, UUID invoiceId, UUID accountId, DateTime date, BigDecimal amount, Currency currency,
+ @Nullable String createdBy, @Nullable DateTime createdDate) {
+ super(id, invoiceId, accountId, null, null, null, null, date, date, amount, currency, createdBy, createdDate);
+ }
+
+ @Override
+ public InvoiceItem asReversingItem() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getDescription() {
+ return "Credit";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ CreditInvoiceItem that = (CreditInvoiceItem) o;
+
+ if (accountId.compareTo(that.accountId) != 0) return false;
+ if (amount.compareTo(that.amount) != 0) return false;
+ if (currency != that.currency) return false;
+ if (startDate.compareTo(that.startDate) != 0) return false;
+ if (endDate.compareTo(that.endDate) != 0) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = accountId.hashCode();
+ result = 31 * result + startDate.hashCode();
+ result = 31 * result + endDate.hashCode();
+ result = 31 * result + amount.hashCode();
+ result = 31 * result + currency.hashCode();
+ return result;
+ }
+
+ @Override
+ public int compareTo(InvoiceItem item) {
+ if (!(item instanceof CreditInvoiceItem)) {
+ return 1;
+ }
+
+ CreditInvoiceItem that = (CreditInvoiceItem) item;
+ return id.compareTo(that.getId());
+ }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
index 627a7be..0e19115 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
@@ -19,8 +19,10 @@ package com.ning.billing.invoice.model;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import javax.annotation.Nullable;
@@ -59,9 +61,9 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
*/
@Override
public Invoice generateInvoice(final UUID accountId, @Nullable final BillingEventSet events,
- @Nullable final List<Invoice> existingInvoices,
- DateTime targetDate,
- final Currency targetCurrency) throws InvoiceApiException {
+ @Nullable final List<Invoice> existingInvoices,
+ DateTime targetDate,
+ final Currency targetCurrency) throws InvoiceApiException {
if ((events == null) || (events.size() == 0)) {
return null;
}
@@ -91,18 +93,90 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
for (InvoiceItem existingItem : existingItems) {
if (existingItem instanceof RecurringInvoiceItem) {
RecurringInvoiceItem recurringItem = (RecurringInvoiceItem) existingItem;
- proposedItems.add(recurringItem.asCredit());
+ proposedItems.add(recurringItem.asReversingItem());
}
}
+ addCreditItems(accountId, proposedItems, existingInvoices, targetCurrency);
+
if (proposedItems == null || proposedItems.size() == 0) {
return null;
} else {
invoice.addInvoiceItems(proposedItems);
+
return invoice;
}
}
+ /*
+ * ensures that the balance of all invoices are zero or positive, adding an adjusting credit item if needed
+ */
+ private void addCreditItems(UUID accountId, List<InvoiceItem> invoiceItems, List<Invoice> invoices, Currency currency) {
+ Map<UUID, BigDecimal> invoiceBalances = new HashMap<UUID, BigDecimal>();
+
+ updateInvoiceBalance(invoiceItems, invoiceBalances);
+
+ // add all existing items and payments
+ for (Invoice invoice : invoices) {
+ updateInvoiceBalance(invoice.getInvoiceItems(), invoiceBalances);
+ }
+
+ for (Invoice invoice : invoices) {
+ UUID invoiceId = invoice.getId();
+ invoiceBalances.put(invoiceId, invoiceBalances.get(invoiceId).subtract(invoice.getAmountPaid()));
+ }
+
+ BigDecimal creditTotal = BigDecimal.ZERO;
+
+ for (UUID invoiceId : invoiceBalances.keySet()) {
+ BigDecimal balance = invoiceBalances.get(invoiceId);
+ if (balance.compareTo(BigDecimal.ZERO) < 0) {
+ creditTotal = creditTotal.add(balance.negate());
+ invoiceItems.add(new CreditInvoiceItem(invoiceId, accountId, clock.getUTCNow(), balance, currency));
+ }
+ }
+
+ if (creditTotal.compareTo(BigDecimal.ZERO) != 0) {
+ // create a single credit item to cover all credits
+ //invoiceItems.add(new CreditInvoiceItem());
+ }
+ }
+
+ private void updateInvoiceBalance(List<InvoiceItem> items, Map<UUID, BigDecimal> invoiceBalances) {
+ for (InvoiceItem item : items) {
+ UUID invoiceId = item.getInvoiceId();
+
+ if (!invoiceBalances.containsKey(invoiceId)) {
+ invoiceBalances.put(invoiceId, BigDecimal.ZERO);
+ }
+
+ invoiceBalances.put(invoiceId, invoiceBalances.get(invoiceId).add(item.getAmount()));
+ }
+ }
+
+ @Override
+ public void distributeItems(List<Invoice> invoices) {
+ Map<UUID, Invoice> invoiceMap = new HashMap<UUID, Invoice>();
+
+ for (Invoice invoice : invoices) {
+ invoiceMap.put(invoice.getId(), invoice);
+ }
+
+ for (final Invoice invoice: invoices) {
+ Iterator<InvoiceItem> itemIterator = invoice.getInvoiceItems().iterator();
+ final UUID invoiceId = invoice.getId();
+
+ while (itemIterator.hasNext()) {
+ InvoiceItem item = itemIterator.next();
+
+ if (!item.getInvoiceId().equals(invoiceId)) {
+ invoiceMap.get(item.getInvoiceId()).addInvoiceItem(item);
+ itemIterator.remove();
+ }
+ }
+ }
+ }
+
private void validateTargetDate(DateTime targetDate) throws InvoiceApiException {
int maximumNumberOfMonths = config.getNumberOfMonthsInFuture();
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/FixedPriceInvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/FixedPriceInvoiceItem.java
index 8c2398b..72566df 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/FixedPriceInvoiceItem.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/FixedPriceInvoiceItem.java
@@ -38,7 +38,7 @@ public class FixedPriceInvoiceItem extends InvoiceItemBase {
}
@Override
- public InvoiceItem asCredit() {
+ public InvoiceItem asReversingItem() {
throw new UnsupportedOperationException();
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceGenerator.java b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceGenerator.java
index 01ebf34..f903087 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceGenerator.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceGenerator.java
@@ -19,6 +19,7 @@ package com.ning.billing.invoice.model;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.api.InvoiceItem;
import org.joda.time.DateTime;
import javax.annotation.Nullable;
@@ -26,5 +27,8 @@ import java.util.List;
import java.util.UUID;
public interface InvoiceGenerator {
- public Invoice generateInvoice(UUID accountId, @Nullable BillingEventSet events, @Nullable List<Invoice> existingInvoices, DateTime targetDate, Currency targetCurrency) throws InvoiceApiException;
+ public Invoice generateInvoice(UUID accountId, @Nullable BillingEventSet events, @Nullable List<Invoice> existingInvoices,
+ DateTime targetDate, Currency targetCurrency) throws InvoiceApiException;
+
+ public void distributeItems(List<Invoice> invoices);
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java
index 175a08e..f659807 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java
@@ -119,7 +119,7 @@ public abstract class InvoiceItemBase extends EntityBase implements InvoiceItem
}
@Override
- public abstract InvoiceItem asCredit();
+ public abstract InvoiceItem asReversingItem();
@Override
public abstract String getDescription();
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItem.java
index 683a610..6fd9608 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItem.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItem.java
@@ -67,7 +67,7 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
}
@Override
- public InvoiceItem asCredit() {
+ public InvoiceItem asReversingItem() {
BigDecimal amountNegated = amount == null ? null : amount.negate();
return new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate,
diff --git a/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatter.java b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
index ae0aa9f..2fad805 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
@@ -38,6 +38,7 @@ import com.ning.billing.invoice.api.InvoicePayment;
import com.ning.billing.invoice.api.formatters.InvoiceFormatter;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.tag.ControlTagType;
import com.ning.billing.util.template.translation.TranslatorConfig;
import com.ning.billing.util.tag.Tag;
import com.ning.billing.util.tag.TagDefinition;
@@ -239,8 +240,13 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
}
@Override
- public boolean hasTag(String tagName) {
- return invoice.hasTag(tagName);
+ public boolean hasTag(TagDefinition tagDefinition) {
+ return invoice.hasTag(tagDefinition);
+ }
+
+ @Override
+ public boolean hasTag(ControlTagType controlTagType) {
+ return invoice.hasTag(controlTagType);
}
@Override
@@ -254,6 +260,11 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
}
@Override
+ public void addTagsFromDefinitions(List<TagDefinition> tagDefinitions) {
+ invoice.addTagsFromDefinitions(tagDefinitions);
+ }
+
+ @Override
public void clearTags() {
invoice.clearTags();
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
index 4ca8ccf..cc072d9 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
@@ -55,8 +55,8 @@ public class DefaultInvoiceItemFormatter implements InvoiceItemFormatter {
}
@Override
- public InvoiceItem asCredit() {
- return item.asCredit();
+ public InvoiceItem asReversingItem() {
+ return item.asReversingItem();
}
@Override
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
index d5edd6e..bb8313a 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
@@ -653,7 +653,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
invoiceDao.addControlTag(ControlTagType.WRITTEN_OFF, invoice.getId(), context);
Invoice savedInvoice = invoiceDao.getById(invoice.getId());
- assertTrue(savedInvoice.hasTag(ControlTagType.WRITTEN_OFF.toString()));
+ assertTrue(savedInvoice.hasTag(ControlTagType.WRITTEN_OFF));
}
@Test
@@ -683,10 +683,10 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
invoiceDao.addControlTag(ControlTagType.WRITTEN_OFF, invoice.getId(), context);
Invoice savedInvoice = invoiceDao.getById(invoice.getId());
- assertTrue(savedInvoice.hasTag(ControlTagType.WRITTEN_OFF.toString()));
+ assertTrue(savedInvoice.hasTag(ControlTagType.WRITTEN_OFF));
invoiceDao.removeControlTag(ControlTagType.WRITTEN_OFF, invoice.getId(), context);
savedInvoice = invoiceDao.getById(invoice.getId());
- assertFalse(savedInvoice.hasTag(ControlTagType.WRITTEN_OFF.toString()));
+ assertFalse(savedInvoice.hasTag(ControlTagType.WRITTEN_OFF));
}
}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
index 1f446d0..b4d9789 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
@@ -22,11 +22,14 @@ import static org.testng.Assert.assertNull;
import java.math.BigDecimal;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.model.DefaultInvoicePayment;
import org.joda.time.DateTime;
import org.testng.annotations.Test;
@@ -719,5 +722,116 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
assertEquals(invoice.getTotalAmount(), expectedAmount);
}
+ @Test(groups = {"fast", "invoicing"})
+ public void testAddOnInvoiceGeneration() throws CatalogApiException, InvoiceApiException {
+ DateTime april25 = new DateTime(2012, 4, 25, 0, 0, 0, 0);
+
+ // create a base plan on April 25th
+ UUID accountId = UUID.randomUUID();
+ Subscription baseSubscription = createZombieSubscription();
+
+ Plan basePlan = new MockPlan("base Plan");
+ MockInternationalPrice price5 = new MockInternationalPrice(new DefaultPrice(FIVE, Currency.USD));
+ MockInternationalPrice price10 = new MockInternationalPrice(new DefaultPrice(TEN, Currency.USD));
+ MockInternationalPrice price20 = new MockInternationalPrice(new DefaultPrice(TWENTY, Currency.USD));
+ PlanPhase basePlanEvergreen = new MockPlanPhase(price10, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
+
+ BillingEventSet events = new BillingEventSet();
+ events.add(createBillingEvent(baseSubscription.getId(), april25, basePlan, basePlanEvergreen, 25));
+
+ // generate invoice
+ Invoice invoice1 = generator.generateInvoice(accountId, events, null, april25, Currency.USD);
+ assertNotNull(invoice1);
+ assertEquals(invoice1.getNumberOfItems(), 1);
+ assertEquals(invoice1.getTotalAmount().compareTo(TEN), 0);
+
+ List<Invoice> invoices = new ArrayList<Invoice>();
+ invoices.add(invoice1);
+
+ // create 2 add ons on April 28th
+ DateTime april28 = new DateTime(2012, 4, 28, 0, 0, 0, 0);
+ Subscription addOnSubscription1 = createZombieSubscription();
+ Plan addOn1Plan = new MockPlan("add on 1");
+ PlanPhase addOn1PlanPhaseEvergreen = new MockPlanPhase(price5, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
+ events.add(createBillingEvent(addOnSubscription1.getId(), april28, addOn1Plan, addOn1PlanPhaseEvergreen, 25));
+
+ Subscription addOnSubscription2 = createZombieSubscription();
+ Plan addOn2Plan = new MockPlan("add on 2");
+ PlanPhase addOn2PlanPhaseEvergreen = new MockPlanPhase(price20, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
+ events.add(createBillingEvent(addOnSubscription2.getId(), april28, addOn2Plan, addOn2PlanPhaseEvergreen, 25));
+
+ // generate invoice
+ Invoice invoice2 = generator.generateInvoice(accountId, events, invoices, april28, Currency.USD);
+ invoices.add(invoice2);
+ assertNotNull(invoice2);
+ assertEquals(invoice2.getNumberOfItems(), 2);
+ assertEquals(invoice2.getTotalAmount().compareTo(TWENTY_FIVE.multiply(new BigDecimal("0.9")).setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD)), 0);
+
+ // perform a repair (change base plan; remove one add-on)
+ // event stream should include just two plans
+ BillingEventSet newEvents = new BillingEventSet();
+ Plan basePlan2 = new MockPlan("base plan 2");
+ MockInternationalPrice price13 = new MockInternationalPrice(new DefaultPrice(THIRTEEN, Currency.USD));
+ PlanPhase basePlan2Phase = new MockPlanPhase(price13, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
+ newEvents.add(createBillingEvent(baseSubscription.getId(), april25, basePlan2, basePlan2Phase, 25));
+ newEvents.add(createBillingEvent(addOnSubscription1.getId(), april28, addOn1Plan, addOn1PlanPhaseEvergreen, 25));
+
+ // generate invoice
+ DateTime may1 = new DateTime(2012, 5, 1, 0, 0, 0, 0);
+ Invoice invoice3 = generator.generateInvoice(accountId, newEvents, invoices, may1, Currency.USD);
+ assertNotNull(invoice3);
+ assertEquals(invoice3.getNumberOfItems(), 5);
+ // -4.50 -18 - 10 (to correct the previous 2 invoices) + 4.50 + 13
+ assertEquals(invoice3.getTotalAmount().compareTo(FIFTEEN.negate()), 0);
+ }
+
+ @Test
+ public void testRepairForPaidInvoice() throws CatalogApiException, InvoiceApiException {
+ // create an invoice
+ DateTime april25 = new DateTime(2012, 4, 25, 0, 0, 0, 0);
+
+ // create a base plan on April 25th
+ UUID accountId = UUID.randomUUID();
+ Subscription originalSubscription = createZombieSubscription();
+
+ Plan originalPlan = new MockPlan("original plan");
+ MockInternationalPrice price10 = new MockInternationalPrice(new DefaultPrice(TEN, Currency.USD));
+ PlanPhase originalPlanEvergreen = new MockPlanPhase(price10, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
+
+ BillingEventSet events = new BillingEventSet();
+ events.add(createBillingEvent(originalSubscription.getId(), april25, originalPlan, originalPlanEvergreen, 25));
+
+ Invoice invoice1 = generator.generateInvoice(accountId, events, null, april25, Currency.USD);
+ List<Invoice> invoices = new ArrayList<Invoice>();
+ invoices.add(invoice1);
+
+ // pay the invoice
+ invoice1.addPayment(new DefaultInvoicePayment(UUID.randomUUID(), invoice1.getId(), april25, TEN, Currency.USD));
+ assertEquals(invoice1.getBalance().compareTo(ZERO), 0);
+
+ // change the plan (i.e. repair) on start date
+ events.clear();
+ Subscription newSubscription = createZombieSubscription();
+ Plan newPlan = new MockPlan("new plan");
+ MockInternationalPrice price5 = new MockInternationalPrice(new DefaultPrice(FIVE, Currency.USD));
+ PlanPhase newPlanEvergreen = new MockPlanPhase(price5, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
+ events.add(createBillingEvent(newSubscription.getId(), april25, newPlan, newPlanEvergreen, 25));
+
+ // generate a new invoice
+ Invoice invoice2 = generator.generateInvoice(accountId, events, invoices, april25, Currency.USD);
+ invoices.add(invoice2);
+
+ // move items to the correct invoice (normally, the dao calls will sort that out)
+ generator.distributeItems(invoices);
+
+ // ensure that the original invoice balance is zero
+
+ assertEquals(invoice1.getBalance().compareTo(ZERO), 0);
+
+ // ensure that the account balance is correct
+ assertEquals(invoice2.getBalance().compareTo(FIVE.negate()), 0);
+
+ }
+
// TODO: Jeff C -- how do we ensure that an annual add-on is properly aligned *at the end* with the base plan?
}
\ No newline at end of file
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java
index 132ac1c..25be5e2 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java
@@ -43,7 +43,7 @@ public abstract class InvoicingTestBase {
protected static final BigDecimal ONE_AND_A_HALF = new BigDecimal("1.5").setScale(NUMBER_OF_DECIMALS);
protected static final BigDecimal TWO = new BigDecimal("2.0").setScale(NUMBER_OF_DECIMALS);
protected static final BigDecimal THREE = new BigDecimal("3.0").setScale(NUMBER_OF_DECIMALS);
- //protected static final BigDecimal FOUR = new BigDecimal("4.0").setScale(NUMBER_OF_DECIMALS);
+ protected static final BigDecimal FOUR = new BigDecimal("4.0").setScale(NUMBER_OF_DECIMALS);
protected static final BigDecimal FIVE = new BigDecimal("5.0").setScale(NUMBER_OF_DECIMALS);
protected static final BigDecimal SIX = new BigDecimal("6.0").setScale(NUMBER_OF_DECIMALS);
protected static final BigDecimal SEVEN = new BigDecimal("7.0").setScale(NUMBER_OF_DECIMALS);
@@ -60,6 +60,7 @@ public abstract class InvoicingTestBase {
protected static final BigDecimal TWENTY = new BigDecimal("20.0").setScale(NUMBER_OF_DECIMALS);
protected static final BigDecimal TWENTY_FOUR = new BigDecimal("24.0").setScale(NUMBER_OF_DECIMALS);
+ protected static final BigDecimal TWENTY_FIVE = new BigDecimal("25.0").setScale(NUMBER_OF_DECIMALS);
protected static final BigDecimal TWENTY_EIGHT = new BigDecimal("28.0").setScale(NUMBER_OF_DECIMALS);
protected static final BigDecimal TWENTY_NINE = new BigDecimal("29.0").setScale(NUMBER_OF_DECIMALS);
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingAccountUserApi.java b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingAccountUserApi.java
index 71231b5..3b35a0c 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingAccountUserApi.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingAccountUserApi.java
@@ -29,10 +29,9 @@ import com.ning.billing.account.api.MigrationAccountData;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.customfield.CustomField;
import com.ning.billing.util.glue.RealImplementation;
-import com.ning.billing.util.tag.Tag;
+import com.ning.billing.util.tag.TagDefinition;
public class BlockingAccountUserApi implements AccountUserApi {
-
private AccountUserApi userApi;
@Inject
@@ -41,15 +40,15 @@ public class BlockingAccountUserApi implements AccountUserApi {
}
@Override
- public Account createAccount(AccountData data, List<CustomField> fields, List<Tag> tags, CallContext context)
+ public Account createAccount(AccountData data, List<CustomField> fields, List<TagDefinition> tagDefinitions, CallContext context)
throws AccountApiException {
- return userApi.createAccount(data, fields, tags, context);
+ return userApi.createAccount(data, fields, tagDefinitions, context);
}
@Override
- public Account migrateAccount(MigrationAccountData data, List<CustomField> fields, List<Tag> tags,
+ public Account migrateAccount(MigrationAccountData data, List<CustomField> fields, List<TagDefinition> tagDefinitions,
CallContext context) throws AccountApiException {
- return userApi.migrateAccount(data, fields, tags, context);
+ return userApi.migrateAccount(data, fields, tagDefinitions, context);
}
@Override
diff --git a/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueState.java b/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueState.java
index 1d76d72..7d84e7c 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueState.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueState.java
@@ -58,7 +58,7 @@ public class DefaultOverdueState<T extends Blockable> extends ValidatingConfig<O
//Other actions could include
// - send email
// - trigger payment retry?
- // - add tags to bundle/account
+ // - add tagStore to bundle/account
// - set payment failure email template
// - set payment retry interval
// - backup payment mechanism?
diff --git a/util/src/main/java/com/ning/billing/util/customfield/dao/AuditedCustomFieldDao.java b/util/src/main/java/com/ning/billing/util/customfield/dao/AuditedCustomFieldDao.java
index b7d2de0..e319117 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/dao/AuditedCustomFieldDao.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/AuditedCustomFieldDao.java
@@ -45,7 +45,7 @@ public class AuditedCustomFieldDao implements CustomFieldDao {
while (existingFieldIterator.hasNext()) {
CustomField existingField = existingFieldIterator.next();
if (field.getName().equals(existingField.getName())) {
- // if the tags match, remove from both lists
+ // if the tagStore match, remove from both lists
fieldsToUpdate.add(field);
fieldIterator.remove();
existingFieldIterator.remove();
diff --git a/util/src/main/java/com/ning/billing/util/entity/ExtendedEntityBase.java b/util/src/main/java/com/ning/billing/util/entity/ExtendedEntityBase.java
index 0017397..2bb03ca 100644
--- a/util/src/main/java/com/ning/billing/util/entity/ExtendedEntityBase.java
+++ b/util/src/main/java/com/ning/billing/util/entity/ExtendedEntityBase.java
@@ -21,6 +21,8 @@ import com.ning.billing.util.customfield.CustomField;
import com.ning.billing.util.customfield.Customizable;
import com.ning.billing.util.customfield.DefaultFieldStore;
import com.ning.billing.util.customfield.FieldStore;
+import com.ning.billing.util.tag.ControlTagType;
+import com.ning.billing.util.tag.DefaultControlTag;
import com.ning.billing.util.tag.DefaultTagStore;
import com.ning.billing.util.tag.DescriptiveTag;
import com.ning.billing.util.tag.Tag;
@@ -30,23 +32,24 @@ import com.ning.billing.util.tag.Taggable;
import org.joda.time.DateTime;
import javax.annotation.Nullable;
+import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public abstract class ExtendedEntityBase extends EntityBase implements Customizable, Taggable {
protected final FieldStore fields;
- protected final TagStore tags;
+ protected final TagStore tagStore;
public ExtendedEntityBase() {
super();
this.fields = DefaultFieldStore.create(getId(), getObjectName());
- this.tags = new DefaultTagStore(id, getObjectName());
+ this.tagStore = new DefaultTagStore(id, getObjectName());
}
public ExtendedEntityBase(final UUID id, @Nullable final String createdBy, @Nullable final DateTime createdDate) {
super(id, createdBy, createdDate);
this.fields = DefaultFieldStore.create(getId(), getObjectName());
- this.tags = new DefaultTagStore(id, getObjectName());
+ this.tagStore = new DefaultTagStore(id, getObjectName());
}
@Override
@@ -78,45 +81,67 @@ public abstract class ExtendedEntityBase extends EntityBase implements Customiza
@Override
public List<Tag> getTagList() {
- return tags.getEntityList();
+ return tagStore.getEntityList();
}
@Override
- public boolean hasTag(final String tagName) {
- return tags.containsTag(tagName);
+ public boolean hasTag(final TagDefinition definition) {
+ return tagStore.containsTagForDefinition(definition);
}
+ @Override
+ public boolean hasTag(ControlTagType controlTagType) {
+ return tagStore.containsTagForControlTagType(controlTagType);
+ }
+
@Override
public void addTag(final TagDefinition definition) {
Tag tag = new DescriptiveTag(definition);
- tags.add(tag) ;
+ tagStore.add(tag) ;
}
+ @Override
+ public void addTags(final List<Tag> tags) {
+ this.tagStore.add(tags);
+ }
+
@Override
- public void addTags(final List<Tag> tags) {
- if (tags != null) {
- this.tags.add(tags);
+ public void addTagsFromDefinitions(final List<TagDefinition> tagDefinitions) {
+ if (tagStore != null) {
+ List<Tag> tags = new ArrayList<Tag>();
+ if (tagDefinitions != null) {
+ for (TagDefinition tagDefinition : tagDefinitions) {
+ try {
+ ControlTagType controlTagType = ControlTagType.valueOf(tagDefinition.getName());
+ tags.add(new DefaultControlTag(controlTagType));
+ } catch (IllegalArgumentException ex) {
+ tags.add(new DescriptiveTag(tagDefinition));
+ }
+ }
+ }
+
+ this.tagStore.add(tags);
}
}
@Override
public void clearTags() {
- this.tags.clear();
+ this.tagStore.clear();
}
@Override
- public void removeTag(final TagDefinition definition) {
- tags.remove(definition.getName());
+ public void removeTag(final TagDefinition tagDefinition) {
+ tagStore.remove(tagDefinition);
}
@Override
public boolean generateInvoice() {
- return tags.generateInvoice();
+ return tagStore.generateInvoice();
}
@Override
public boolean processPayment() {
- return tags.processPayment();
+ return tagStore.processPayment();
}
@Override
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/AuditedTagDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/AuditedTagDao.java
index 9798250..3b238c0 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/AuditedTagDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/AuditedTagDao.java
@@ -52,10 +52,10 @@ public class AuditedTagDao extends AuditedDaoBase implements TagDao {
final List<Tag> tags, final CallContext context) {
TagSqlDao tagSqlDao = dao.become(TagSqlDao.class);
- // get list of existing tags
+ // get list of existing tagStore
List<Tag> existingTags = tagSqlDao.load(objectId.toString(), objectType);
- // sort into tags to update (tagsToUpdate), tags to add (tags), and tags to delete (existingTags)
+ // sort into tagStore to update (tagsToUpdate), tagStore to add (tagStore), and tagStore to delete (existingTags)
Iterator<Tag> tagIterator = tags.iterator();
while (tagIterator.hasNext()) {
Tag tag = tagIterator.next();
@@ -64,7 +64,7 @@ public class AuditedTagDao extends AuditedDaoBase implements TagDao {
while (existingTagIterator.hasNext()) {
Tag existingTag = existingTagIterator.next();
if (tag.getTagDefinitionName().equals(existingTag.getTagDefinitionName())) {
- // if the tags match, remove from both lists
+ // if the tagStore match, remove from both lists
// in the case of tag, this just means the tag remains associated
tagIterator.remove();
existingTagIterator.remove();
diff --git a/util/src/main/java/com/ning/billing/util/tag/DefaultTagStore.java b/util/src/main/java/com/ning/billing/util/tag/DefaultTagStore.java
index eace017..aa9c3af 100644
--- a/util/src/main/java/com/ning/billing/util/tag/DefaultTagStore.java
+++ b/util/src/main/java/com/ning/billing/util/tag/DefaultTagStore.java
@@ -32,7 +32,7 @@ public class DefaultTagStore extends EntityCollectionBase<Tag> implements TagSto
@Override
/***
* Collates the contents of the TagStore to determine if payments should be processed
- * @return true is no tags contraindicate payment processing
+ * @return true if no tags contraindicate payment processing
*/
public boolean processPayment() {
for (Tag tag : entities.values()) {
@@ -49,7 +49,7 @@ public class DefaultTagStore extends EntityCollectionBase<Tag> implements TagSto
/***
* Collates the contents of the TagStore to determine if invoices should be generated
- * @return true is no tags contraindicate invoice generation
+ * @return true if no tags contraindicate invoice generation
*/
@Override
public boolean generateInvoice() {
@@ -66,18 +66,30 @@ public class DefaultTagStore extends EntityCollectionBase<Tag> implements TagSto
}
@Override
- public void remove(final String tagName) {
- entities.remove(entities.get(tagName));
+ public boolean containsTagForDefinition(final TagDefinition tagDefinition) {
+ for (Tag tag : entities.values()) {
+ if (tag.getTagDefinitionName().equals(tagDefinition.getName())) {
+ return true;
+ }
+ }
+
+ return false;
}
@Override
- public boolean containsTag(final String tagName) {
+ public boolean containsTagForControlTagType(final ControlTagType controlTagType) {
for (Tag tag : entities.values()) {
- if (tag.getTagDefinitionName().equals(tagName)) {
+ if (tag.getTagDefinitionName().equals(controlTagType.toString())) {
return true;
}
}
return false;
}
+
+ @Override
+ public Tag remove(TagDefinition tagDefinition) {
+ Tag tag = entities.get(tagDefinition.getName());
+ return (tag == null) ? null : entities.remove(tag);
+ }
}
\ No newline at end of file
diff --git a/util/src/main/resources/com/ning/billing/util/ddl.sql b/util/src/main/resources/com/ning/billing/util/ddl.sql
index e5e9488..4d91d1c 100644
--- a/util/src/main/resources/com/ning/billing/util/ddl.sql
+++ b/util/src/main/resources/com/ning/billing/util/ddl.sql
@@ -119,5 +119,7 @@ CREATE TABLE audit_log (
user_token char(36),
PRIMARY KEY(id)
) ENGINE=innodb;
+CREATE INDEX audit_log_fetch_record ON audit_log(table_name, record_id);
+CREATE INDEX audit_log_user_name ON audit_log(changed_by);
diff --git a/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java b/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java
index e29023f..0e5041a 100644
--- a/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java
+++ b/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java
@@ -115,7 +115,7 @@ public class TestTagStore {
public Void withHandle(Handle handle) throws Exception {
handle.createScript("delete from tag_definitions").execute();
handle.createScript("delete from tag_definition_history").execute();
- handle.createScript("delete from tags").execute();
+ handle.createScript("delete from tagStore").execute();
handle.createScript("delete from tag_history").execute();
return null;
}
@@ -309,7 +309,7 @@ public class TestTagStore {
try {
tagDefinitionDao.deleteAllTagsForDefinition(definitionName, context);
} catch (TagDefinitionApiException e) {
- fail("Could not delete tags for tag definition", e);
+ fail("Could not delete tagStore for tag definition", e);
}
try {
@@ -334,7 +334,7 @@ public class TestTagStore {
try {
tagDefinitionDao.deleteAllTagsForDefinition(definitionName, context);
} catch (TagDefinitionApiException e) {
- fail("Could not delete tags for tag definition", e);
+ fail("Could not delete tagStore for tag definition", e);
}
try {