killbill-memoizeit
Changes
invoice/pom.xml 7(+0 -7)
invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatter.java 286(+286 -0)
invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatterFactory.java 31(+31 -0)
invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java 136(+136 -0)
invoice/src/main/java/com/ning/billing/invoice/template/translator/DefaultInvoiceTranslator.java 136(+136 -0)
invoice/src/test/java/com/ning/billing/invoice/api/migration/MockModuleNoEntitlement.java 30(+15 -15)
invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java 24(+14 -10)
invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java 9(+4 -5)
invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java 235(+178 -57)
junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingAccountUserApi.java 21(+16 -5)
junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultEntitlementBillingApi.java 4(+2 -2)
pom.xml 20(+18 -2)
util/pom.xml 16(+16 -0)
util/src/main/java/com/ning/billing/util/template/translation/DefaultCatalogTranslator.java 36(+36 -0)
util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldHistorySqlDao.sql.stg 4(+2 -2)
util/src/main/resources/com/ning/billing/util/email/templates/HtmlInvoiceTemplate.mustache 96(+96 -0)
util/src/main/resources/com/ning/billing/util/template/translation/CatalogTranslation_EN_US.properties 2(+2 -0)
util/src/main/resources/com/ning/billing/util/template/translation/CatalogTranslation_FR_CA.properties 1(+1 -0)
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 6eab3f3..7fc3a32 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
@@ -48,6 +48,8 @@ public class DefaultAccount extends ExtendedEntityBase implements Account {
private final String country;
private final String postalCode;
private final String phone;
+ private final boolean isMigrated;
+ private final boolean isNotifiedForInvoices;
private final String updatedBy;
private final DateTime updatedDate;
@@ -81,33 +83,14 @@ public class DefaultAccount extends ExtendedEntityBase implements Account {
data.getTimeZone(), data.getLocale(),
data.getAddress1(), data.getAddress2(), data.getCompanyName(),
data.getCity(), data.getStateOrProvince(), data.getCountry(),
- data.getPostalCode(), data.getPhone(), createdBy, createdDate,
+ data.getPostalCode(), data.getPhone(), data.isMigrated(), data.isNotifiedForInvoices(),
+ createdBy, createdDate,
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 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,
@@ -115,6 +98,7 @@ public class DefaultAccount extends ExtendedEntityBase implements Account {
final String address1, final String address2, final String companyName,
final String city, final String stateOrProvince, final String country,
final String postalCode, final String phone,
+ final boolean isMigrated, final boolean isNotifiedForInvoices,
final String createdBy, final DateTime createdDate,
final String updatedBy, final DateTime updatedDate) {
@@ -136,6 +120,8 @@ public class DefaultAccount extends ExtendedEntityBase implements Account {
this.postalCode = postalCode;
this.country = country;
this.phone = phone;
+ this.isMigrated = isMigrated;
+ this.isNotifiedForInvoices = isNotifiedForInvoices;
this.updatedBy = updatedBy;
this.updatedDate = updatedDate;
}
@@ -241,6 +227,16 @@ public class DefaultAccount extends ExtendedEntityBase implements Account {
}
@Override
+ public boolean isMigrated() {
+ return this.isMigrated;
+ }
+
+ @Override
+ public boolean isNotifiedForInvoices() {
+ return isNotifiedForInvoices;
+ }
+
+ @Override
public String getUpdatedBy() {
return updatedBy;
}
@@ -279,7 +275,7 @@ public class DefaultAccount extends ExtendedEntityBase implements Account {
", stateOrProvince=" + stateOrProvince +
", postalCode=" + postalCode +
", country=" + country +
- ", tags=" + tags +
+ ", tags=" + tagStore +
", fields=" + fields +
"]";
}
diff --git a/account/src/main/java/com/ning/billing/account/api/DefaultAccountEmail.java b/account/src/main/java/com/ning/billing/account/api/DefaultAccountEmail.java
new file mode 100644
index 0000000..dd9323d
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/api/DefaultAccountEmail.java
@@ -0,0 +1,54 @@
+/*
+ * 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.account.api;
+
+import com.ning.billing.util.entity.UpdatableEntityBase;
+import org.joda.time.DateTime;
+
+import java.util.UUID;
+
+public class DefaultAccountEmail extends UpdatableEntityBase implements AccountEmail {
+ private final UUID accountId;
+ private final String email;
+
+ public DefaultAccountEmail(UUID accountId, String email) {
+ super();
+ this.accountId = accountId;
+ this.email = email;
+ }
+
+ public DefaultAccountEmail(AccountEmail source, String newEmail) {
+ this(source.getId(), source.getAccountId(), newEmail,
+ source.getCreatedBy(), source.getCreatedDate(), source.getUpdatedBy(), source.getUpdatedDate());
+ }
+
+ public DefaultAccountEmail(UUID id, UUID accountId, String email, String createdBy, DateTime createdDate, String updatedBy, DateTime updatedDate) {
+ super(id, createdBy, createdDate, updatedBy, updatedDate);
+ this.accountId = accountId;
+ this.email = email;
+ }
+
+ @Override
+ public UUID getAccountId() {
+ return accountId;
+ }
+
+ @Override
+ public String getEmail() {
+ return email;
+ }
+}
diff --git a/account/src/main/java/com/ning/billing/account/api/DefaultMutableAccountData.java b/account/src/main/java/com/ning/billing/account/api/DefaultMutableAccountData.java
index 4476130..0f72d41 100644
--- a/account/src/main/java/com/ning/billing/account/api/DefaultMutableAccountData.java
+++ b/account/src/main/java/com/ning/billing/account/api/DefaultMutableAccountData.java
@@ -16,13 +16,12 @@
package com.ning.billing.account.api;
-import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.util.tag.TagStore;
-public class DefaultMutableAccountData implements AccountData, MutableAccountData {
+public class DefaultMutableAccountData implements MutableAccountData {
private String externalKey;
private String email;
private String name;
@@ -40,6 +39,8 @@ public class DefaultMutableAccountData implements AccountData, MutableAccountDat
private String country;
private String postalCode;
private String phone;
+ private boolean isMigrated;
+ private boolean isNotifiedForInvoices;
public DefaultMutableAccountData(String externalKey, String email, String name,
int firstNameLength, Currency currency, int billCycleDay,
@@ -47,7 +48,7 @@ public class DefaultMutableAccountData implements AccountData, MutableAccountDat
String locale, String address1, String address2,
String companyName, String city, String stateOrProvince,
String country, String postalCode, String phone,
- DateTime createdDate, DateTime updatedDate) {
+ boolean isMigrated, boolean isNotifiedForInvoices) {
super();
this.externalKey = externalKey;
this.email = email;
@@ -66,6 +67,8 @@ public class DefaultMutableAccountData implements AccountData, MutableAccountDat
this.country = country;
this.postalCode = postalCode;
this.phone = phone;
+ this.isMigrated = isMigrated;
+ this.isNotifiedForInvoices = isNotifiedForInvoices;
}
public DefaultMutableAccountData(AccountData accountData) {
@@ -87,6 +90,8 @@ public class DefaultMutableAccountData implements AccountData, MutableAccountDat
this.country = accountData.getCountry();
this.postalCode = accountData.getPostalCode();
this.phone = accountData.getPhone();
+ this.isMigrated = accountData.isMigrated();
+ this.isNotifiedForInvoices = accountData.isNotifiedForInvoices();
}
/* (non-Javadoc)
@@ -208,7 +213,20 @@ public class DefaultMutableAccountData implements AccountData, MutableAccountDat
public String getPhone() {
return phone;
}
-
+ /* (non-Javadoc)
+ * @see com.ning.billing.account.api.MutableAccountData#isMigrated()
+ */
+ @Override
+ public boolean isMigrated() {
+ return isMigrated;
+ }
+ /* (non-Javadoc)
+ * @see com.ning.billing.account.api.MutableAccountData#getSendInvoiceEmails()
+ */
+ @Override
+ public boolean isNotifiedForInvoices() {
+ return isNotifiedForInvoices;
+ }
/* (non-Javadoc)
* @see com.ning.billing.account.api.MutableAccountData#setExternalKey(java.lang.String)
*/
@@ -329,5 +347,15 @@ public class DefaultMutableAccountData implements AccountData, MutableAccountDat
this.phone = phone;
}
+ @Override
+ public void setIsMigrated(boolean isMigrated) {
+ this.isMigrated = isMigrated;
+ }
+
+ @Override
+ public void setIsNotifiedForInvoices(boolean isNotifiedForInvoices) {
+ this.isNotifiedForInvoices = isNotifiedForInvoices;
+ }
+
}
diff --git a/account/src/main/java/com/ning/billing/account/api/user/AccountBuilder.java b/account/src/main/java/com/ning/billing/account/api/user/AccountBuilder.java
index ed18ffb..a6d3840 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/AccountBuilder.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/AccountBuilder.java
@@ -43,6 +43,8 @@ public class AccountBuilder {
private String country;
private String postalCode;
private String phone;
+ private boolean migrated;
+ private boolean isNotifiedForInvoices;
private String createdBy;
private DateTime createdDate;
private String updatedBy;
@@ -141,6 +143,16 @@ public class AccountBuilder {
return this;
}
+ public AccountBuilder migrated(final boolean migrated) {
+ this.migrated = migrated;
+ return this;
+ }
+
+ public AccountBuilder isNotifiedForInvoices(final boolean isNotifiedForInvoices) {
+ this.isNotifiedForInvoices = isNotifiedForInvoices;
+ return this;
+ }
+
public AccountBuilder createdBy(final String createdBy) {
this.createdBy = createdBy;
return this;
@@ -166,6 +178,7 @@ public class AccountBuilder {
currency, billingCycleDay, paymentProviderName,
timeZone, locale,
address1, address2, companyName, city, stateOrProvince, country,
- postalCode, phone, createdBy, createdDate, updatedBy, updatedDate);
+ postalCode, phone, migrated, isNotifiedForInvoices,
+ createdBy, createdDate, updatedBy, updatedDate);
}
}
diff --git a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java
index 2a1b54a..f944ccb 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java
@@ -20,7 +20,6 @@ import com.ning.billing.account.api.Account;
import com.ning.billing.account.api.AccountCreationEvent;
import com.ning.billing.account.api.AccountData;
import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.util.bus.BusEvent.BusEventType;
import java.util.UUID;
@@ -129,6 +128,8 @@ public class DefaultAccountCreationEvent implements AccountCreationEvent {
private final String postalCode;
private final String country;
private final String phone;
+ private final boolean isMigrated;
+ private final boolean isNotifiedForInvoices;
@JsonCreator
public DefaultAccountData(@JsonProperty("externalKey") String externalKey,
@@ -147,7 +148,9 @@ public class DefaultAccountCreationEvent implements AccountCreationEvent {
@JsonProperty("stateOrProvince") String stateOrProvince,
@JsonProperty("postalCode") String postalCode,
@JsonProperty("country") String country,
- @JsonProperty("phone") String phone) {
+ @JsonProperty("phone") String phone,
+ @JsonProperty("isMigrated") boolean isMigrated,
+ @JsonProperty("isNotifiedForInvoices") boolean isNotifiedForInvoices) {
super();
this.externalKey = externalKey;
this.name = name;
@@ -166,6 +169,8 @@ public class DefaultAccountCreationEvent implements AccountCreationEvent {
this.postalCode = postalCode;
this.country = country;
this.phone = phone;
+ this.isMigrated = isMigrated;
+ this.isNotifiedForInvoices = isNotifiedForInvoices;
}
@Override
@@ -214,7 +219,6 @@ public class DefaultAccountCreationEvent implements AccountCreationEvent {
return timeZone;
}
-
@Override
public String getLocale() {
return locale;
@@ -261,6 +265,18 @@ public class DefaultAccountCreationEvent implements AccountCreationEvent {
}
@Override
+ @JsonIgnore
+ public boolean isMigrated() {
+ return isMigrated;
+ }
+
+ @Override
+ @JsonIgnore
+ public boolean isNotifiedForInvoices() {
+ return isNotifiedForInvoices;
+ }
+
+ @Override
public int hashCode() {
final int prime = 31;
int result = 1;
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 1e5a8a6..e8df191 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
@@ -26,6 +26,8 @@ import com.ning.billing.ErrorCode;
import com.ning.billing.account.api.Account;
import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.account.api.AccountData;
+import com.ning.billing.account.api.AccountEmail;
+import com.ning.billing.account.api.AccountUserApi;
import com.ning.billing.account.api.DefaultAccount;
import com.ning.billing.account.api.MigrationAccountData;
import com.ning.billing.account.dao.AccountDao;
@@ -33,9 +35,9 @@ 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;
-public class DefaultAccountUserApi implements com.ning.billing.account.api.AccountUserApi {
+public class DefaultAccountUserApi implements AccountUserApi {
private final CallContextFactory factory;
private final AccountDao dao;
@@ -47,10 +49,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);
@@ -108,7 +110,6 @@ public class DefaultAccountUserApi implements com.ning.billing.account.api.Accou
} catch (EntityPersistenceException e) {
throw new AccountApiException(e, e.getCode(), e.getMessage());
}
-
}
@Override
@@ -122,14 +123,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);
@@ -140,5 +141,13 @@ public class DefaultAccountUserApi implements com.ning.billing.account.api.Accou
return account;
}
+ @Override
+ public List<AccountEmail> getEmails(final UUID accountId) {
+ return dao.getEmails(accountId);
+ }
+ @Override
+ public void saveEmails(final UUID accountId, final List<AccountEmail> newEmails, final CallContext context) {
+ dao.saveEmails(accountId, newEmails, context);
+ }
}
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountBinder.java b/account/src/main/java/com/ning/billing/account/dao/AccountBinder.java
index a4a00a8..57b61c2 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountBinder.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountBinder.java
@@ -60,6 +60,8 @@ public @interface AccountBinder {
q.bind("country", account.getCountry());
q.bind("postalCode", account.getPostalCode());
q.bind("phone", account.getPhone());
+ q.bind("migrated", account.isMigrated());
+ q.bind("isNotifiedForInvoices", account.isNotifiedForInvoices());
}
};
}
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountDao.java
index 4be1058..00f785f 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountDao.java
@@ -16,12 +16,16 @@
package com.ning.billing.account.dao;
+import java.util.List;
import java.util.UUID;
import com.ning.billing.account.api.Account;
import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.account.api.AccountEmail;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.entity.UpdatableEntityDao;
+import javax.annotation.Nullable;
+
public interface AccountDao extends UpdatableEntityDao<Account> {
public Account getAccountByKey(String key);
@@ -32,4 +36,8 @@ public interface AccountDao extends UpdatableEntityDao<Account> {
* @throws AccountApiException when externalKey is null
*/
public UUID getIdFromKey(String externalKey) throws AccountApiException;
+
+ public List<AccountEmail> getEmails(UUID accountId);
+
+ public void saveEmails(UUID accountId, List<AccountEmail> emails, CallContext context);
}
\ No newline at end of file
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountEmailBinder.java b/account/src/main/java/com/ning/billing/account/dao/AccountEmailBinder.java
new file mode 100644
index 0000000..bd71364
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountEmailBinder.java
@@ -0,0 +1,48 @@
+/*
+ * 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.account.dao;
+
+import com.ning.billing.account.api.AccountEmail;
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BinderFactory;
+import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@BindingAnnotation(AccountEmailBinder.AccountEmailBinderFactory.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.PARAMETER})
+public @interface AccountEmailBinder {
+ public static class AccountEmailBinderFactory implements BinderFactory {
+ @Override
+ public Binder<AccountEmailBinder, AccountEmail> build(Annotation annotation) {
+ return new Binder<AccountEmailBinder, AccountEmail>() {
+ @Override
+ public void bind(@SuppressWarnings("rawtypes") SQLStatement q, AccountEmailBinder bind, AccountEmail accountEmail) {
+ q.bind("id", accountEmail.getId().toString());
+ q.bind("accountId", accountEmail.getAccountId().toString());
+ q.bind("email", accountEmail.getEmail());
+ }
+ };
+ }
+ }
+}
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountEmailMapper.java b/account/src/main/java/com/ning/billing/account/dao/AccountEmailMapper.java
new file mode 100644
index 0000000..9444941
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountEmailMapper.java
@@ -0,0 +1,44 @@
+/*
+ * 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.account.dao;
+
+import com.ning.billing.account.api.AccountEmail;
+import com.ning.billing.account.api.DefaultAccountEmail;
+import com.ning.billing.util.dao.MapperBase;
+import org.joda.time.DateTime;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.tweak.ResultSetMapper;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.UUID;
+
+public class AccountEmailMapper extends MapperBase implements ResultSetMapper<AccountEmail> {
+ @Override
+ public AccountEmail map(int index, ResultSet result, StatementContext context) throws SQLException {
+ UUID id = UUID.fromString(result.getString("id"));
+ UUID accountId = UUID.fromString(result.getString("account_id"));
+ String email = result.getString("email");
+
+ String createdBy = result.getString("created_by");
+ DateTime createdDate = getDate(result, "created_date");
+ String updatedBy = result.getString("updated_by");
+ DateTime updatedDate = getDate(result, "updated_date");
+
+ return new DefaultAccountEmail(id, accountId, email, createdBy, createdDate, updatedBy, updatedDate);
+ }
+}
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountEmailSqlDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountEmailSqlDao.java
new file mode 100644
index 0000000..1902154
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountEmailSqlDao.java
@@ -0,0 +1,73 @@
+/*
+ * 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.account.dao;
+
+import com.ning.billing.account.api.AccountEmail;
+import com.ning.billing.util.ChangeType;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
+import com.ning.billing.util.dao.ChangeTypeBinder;
+import com.ning.billing.util.entity.UpdatableEntityDao;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.SqlBatch;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
+import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+
+import java.util.List;
+
+@ExternalizedSqlViaStringTemplate3
+@RegisterMapper(AccountEmailMapper.class)
+public interface AccountEmailSqlDao extends UpdatableEntityDao<AccountEmail>, Transactional<AccountEmailSqlDao>, Transmogrifier {
+ @Override
+ @SqlUpdate
+ public void create(@AccountEmailBinder final AccountEmail accountEmail,
+ @CallContextBinder final CallContext context);
+
+ @SqlBatch(transactional = false)
+ public void create(@AccountEmailBinder final List<AccountEmail> accountEmailList,
+ @CallContextBinder final CallContext context);
+
+ @Override
+ @SqlUpdate
+ public void update(@AccountEmailBinder final AccountEmail accountEmail,
+ @CallContextBinder final CallContext context);
+
+ @SqlBatch(transactional = false)
+ public void update(@AccountEmailBinder final List<AccountEmail> accountEmailList,
+ @CallContextBinder final CallContext context);
+
+ @SqlUpdate
+ public void delete(@AccountEmailBinder final AccountEmail accountEmail,
+ @CallContextBinder final CallContext context);
+
+ @SqlBatch(transactional = false)
+ public void delete(@AccountEmailBinder final List<AccountEmail> accountEmailList,
+ @CallContextBinder final CallContext context);
+
+ @SqlBatch(transactional=false)
+ public void insertAccountEmailHistoryFromTransaction(@Bind("historyRecordId") final List<String> historyRecordIdList,
+ @AccountEmailBinder final List<AccountEmail> accountEmail,
+ @ChangeTypeBinder final ChangeType changeType,
+ @CallContextBinder final CallContext context);
+
+ @SqlQuery
+ public List<AccountEmail> getByAccountId(@Bind("accountId") final String accountId);
+}
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountMapper.java b/account/src/main/java/com/ning/billing/account/dao/AccountMapper.java
new file mode 100644
index 0000000..af8be67
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountMapper.java
@@ -0,0 +1,84 @@
+/*
+ * 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.account.dao;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.user.AccountBuilder;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.dao.MapperBase;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.tweak.ResultSetMapper;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.UUID;
+
+public class AccountMapper extends MapperBase implements ResultSetMapper<Account> {
+ @Override
+ public Account map(int index, ResultSet result, StatementContext context) throws SQLException {
+ UUID id = UUID.fromString(result.getString("id"));
+ String externalKey = result.getString("external_key");
+ String email = result.getString("email");
+ String name = result.getString("name");
+ int firstNameLength = result.getInt("first_name_length");
+ int billingCycleDay = result.getInt("billing_cycle_day");
+
+ String currencyString = result.getString("currency");
+ Currency currency = (currencyString == null) ? null : Currency.valueOf(currencyString);
+
+ String paymentProviderName = result.getString("payment_provider_name");
+
+ String timeZoneId = result.getString("time_zone");
+ DateTimeZone timeZone = (timeZoneId == null) ? null : DateTimeZone.forID(timeZoneId);
+
+ String locale = result.getString("locale");
+
+ String address1 = result.getString("address1");
+ String address2 = result.getString("address2");
+ String companyName = result.getString("company_name");
+ String city = result.getString("city");
+ String stateOrProvince = result.getString("state_or_province");
+ String postalCode = result.getString("postal_code");
+ String country = result.getString("country");
+ String phone = result.getString("phone");
+
+ Boolean migrated = result.getBoolean("migrated");
+ Boolean isNotifiedForInvoices = result.getBoolean("is_notified_for_invoices");
+
+ String createdBy = result.getString("created_by");
+ DateTime createdDate = getDate(result, "created_date");
+ String updatedBy = result.getString("updated_by");
+ DateTime updatedDate = getDate(result, "updated_date");
+
+ return new AccountBuilder(id).externalKey(externalKey).email(email)
+ .name(name).firstNameLength(firstNameLength)
+ .phone(phone).currency(currency)
+ .billingCycleDay(billingCycleDay)
+ .paymentProviderName(paymentProviderName)
+ .timeZone(timeZone).locale(locale)
+ .address1(address1).address2(address2)
+ .companyName(companyName)
+ .city(city).stateOrProvince(stateOrProvince)
+ .postalCode(postalCode).country(country)
+ .migrated(migrated).isNotifiedForInvoices(isNotifiedForInvoices)
+ .createdBy(createdBy).createdDate(createdDate)
+ .updatedBy(updatedBy).updatedDate(updatedDate)
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
index 8c09af4..298ed72 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
@@ -16,17 +16,13 @@
package com.ning.billing.account.dao;
-import java.sql.ResultSet;
-import java.sql.SQLException;
import java.util.UUID;
+import com.ning.billing.util.ChangeType;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.callcontext.CallContextBinder;
-import com.ning.billing.util.dao.MapperBase;
+import com.ning.billing.util.dao.ChangeTypeBinder;
import com.ning.billing.util.entity.UpdatableEntityDao;
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
-import org.skife.jdbi.v2.StatementContext;
import org.skife.jdbi.v2.sqlobject.Bind;
import org.skife.jdbi.v2.sqlobject.SqlQuery;
import org.skife.jdbi.v2.sqlobject.SqlUpdate;
@@ -34,15 +30,12 @@ import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
-import org.skife.jdbi.v2.tweak.ResultSetMapper;
import com.ning.billing.account.api.Account;
-import com.ning.billing.account.api.user.AccountBuilder;
-import com.ning.billing.catalog.api.Currency;
import com.ning.billing.util.UuidMapper;
@ExternalizedSqlViaStringTemplate3
-@RegisterMapper({UuidMapper.class, AccountSqlDao.AccountMapper.class})
+@RegisterMapper({UuidMapper.class, AccountMapper.class})
public interface AccountSqlDao extends UpdatableEntityDao<Account>, Transactional<AccountSqlDao>, Transmogrifier {
@SqlQuery
public Account getAccountByKey(@Bind("externalKey") final String key);
@@ -58,53 +51,9 @@ public interface AccountSqlDao extends UpdatableEntityDao<Account>, Transactiona
@SqlUpdate
public void update(@AccountBinder Account account, @CallContextBinder final CallContext context);
- public static class AccountMapper extends MapperBase implements ResultSetMapper<Account> {
- @Override
- public Account map(int index, ResultSet result, StatementContext context) throws SQLException {
- UUID id = UUID.fromString(result.getString("id"));
- String externalKey = result.getString("external_key");
- String email = result.getString("email");
- String name = result.getString("name");
- int firstNameLength = result.getInt("first_name_length");
- int billingCycleDay = result.getInt("billing_cycle_day");
-
- String currencyString = result.getString("currency");
- Currency currency = (currencyString == null) ? null : Currency.valueOf(currencyString);
-
- String paymentProviderName = result.getString("payment_provider_name");
-
- String timeZoneId = result.getString("time_zone");
- DateTimeZone timeZone = (timeZoneId == null) ? null : DateTimeZone.forID(timeZoneId);
-
- String locale = result.getString("locale");
-
- String address1 = result.getString("address1");
- String address2 = result.getString("address2");
- String companyName = result.getString("company_name");
- String city = result.getString("city");
- String stateOrProvince = result.getString("state_or_province");
- String postalCode = result.getString("postal_code");
- String country = result.getString("country");
- String phone = result.getString("phone");
-
- String createdBy = result.getString("created_by");
- DateTime createdDate = getDate(result, "created_date");
- String updatedBy = result.getString("updated_by");
- DateTime updatedDate = getDate(result, "updated_date");
-
- return new AccountBuilder(id).externalKey(externalKey).email(email)
- .name(name).firstNameLength(firstNameLength)
- .phone(phone).currency(currency)
- .billingCycleDay(billingCycleDay)
- .paymentProviderName(paymentProviderName)
- .timeZone(timeZone).locale(locale)
- .address1(address1).address2(address2)
- .companyName(companyName)
- .city(city).stateOrProvince(stateOrProvince)
- .postalCode(postalCode).country(country)
- .createdBy(createdBy).createdDate(createdDate)
- .updatedBy(updatedBy).updatedDate(updatedDate)
- .build();
- }
- }
+ @SqlUpdate
+ public void insertAccountHistoryFromTransaction(@AccountBinder final Account account,
+ @Bind("historyRecordId") final String historyRecordId,
+ @ChangeTypeBinder ChangeType changeType,
+ @CallContextBinder CallContext context);
}
diff --git a/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java b/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java
index aa628ca..b68ae6f 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java
@@ -17,13 +17,17 @@
package com.ning.billing.account.dao;
import java.sql.DataTruncation;
+import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import java.util.UUID;
+import com.ning.billing.account.api.AccountEmail;
import com.ning.billing.util.ChangeType;
import com.ning.billing.util.audit.dao.AuditSqlDao;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.customfield.dao.CustomFieldDao;
+import com.ning.billing.util.dao.AuditedDaoBase;
import com.ning.billing.util.entity.EntityPersistenceException;
import com.ning.billing.util.tag.dao.TagDao;
import org.skife.jdbi.v2.IDBI;
@@ -42,8 +46,10 @@ import com.ning.billing.util.customfield.dao.CustomFieldSqlDao;
import com.ning.billing.util.bus.Bus;
import com.ning.billing.util.tag.Tag;
-public class AuditedAccountDao implements AccountDao {
+public class AuditedAccountDao extends AuditedDaoBase implements AccountDao {
+ private static final String ACCOUNT_EMAIL_HISTORY_TABLE = "account_email_history";
private final AccountSqlDao accountSqlDao;
+ private final AccountEmailSqlDao accountEmailSqlDao;
private final TagDao tagDao;
private final CustomFieldDao customFieldDao;
private final Bus eventBus;
@@ -52,6 +58,7 @@ public class AuditedAccountDao implements AccountDao {
public AuditedAccountDao(IDBI dbi, Bus eventBus, TagDao tagDao, CustomFieldDao customFieldDao) {
this.eventBus = eventBus;
this.accountSqlDao = dbi.onDemand(AccountSqlDao.class);
+ this.accountEmailSqlDao = dbi.onDemand(AccountEmailSqlDao.class);
this.tagDao = tagDao;
this.customFieldDao = customFieldDao;
}
@@ -124,9 +131,7 @@ public class AuditedAccountDao implements AccountDao {
transactionalDao.create(account, context);
UUID historyId = UUID.randomUUID();
- AccountHistorySqlDao historyDao = accountSqlDao.become(AccountHistorySqlDao.class);
- historyDao.insertAccountHistoryFromTransaction(account, historyId.toString(),
- ChangeType.INSERT.toString(), context);
+ accountSqlDao.insertAccountHistoryFromTransaction(account, historyId.toString(), ChangeType.INSERT, context);
AuditSqlDao auditDao = accountSqlDao.become(AuditSqlDao.class);
auditDao.insertAuditFromTransaction("account_history", historyId.toString(),
@@ -170,12 +175,10 @@ public class AuditedAccountDao implements AccountDao {
accountSqlDao.update(account, context);
UUID historyId = UUID.randomUUID();
- AccountHistorySqlDao historyDao = accountSqlDao.become(AccountHistorySqlDao.class);
- historyDao.insertAccountHistoryFromTransaction(account, historyId.toString(), ChangeType.UPDATE.toString(), context);
+ accountSqlDao.insertAccountHistoryFromTransaction(account, historyId.toString(), ChangeType.UPDATE, context);
AuditSqlDao auditDao = accountSqlDao.become(AuditSqlDao.class);
- auditDao.insertAuditFromTransaction("account_history" ,historyId.toString(),
- ChangeType.INSERT, context);
+ auditDao.insertAuditFromTransaction("account_history" ,historyId.toString(), ChangeType.INSERT, context);
saveTagsFromWithinTransaction(account, accountSqlDao, context);
saveCustomFieldsFromWithinTransaction(account, accountSqlDao, context);
@@ -197,6 +200,64 @@ public class AuditedAccountDao implements AccountDao {
}
@Override
+ public List<AccountEmail> getEmails(final UUID accountId) {
+ return accountEmailSqlDao.getByAccountId(accountId.toString());
+ }
+
+ @Override
+ public void saveEmails(final UUID accountId, final List<AccountEmail> emails, final CallContext context) {
+ final List<AccountEmail> existingEmails = accountEmailSqlDao.getByAccountId(accountId.toString());
+ final List<AccountEmail> updatedEmails = new ArrayList<AccountEmail>();
+
+ Iterator<AccountEmail> existingEmailIterator = existingEmails.iterator();
+ while (existingEmailIterator.hasNext()) {
+ AccountEmail existingEmail = existingEmailIterator.next();
+
+ Iterator<AccountEmail> newEmailIterator = emails.iterator();
+ while (newEmailIterator.hasNext()) {
+ AccountEmail newEmail = newEmailIterator.next();
+ if (newEmail.getId().equals(existingEmail.getId())) {
+ // check equality; if not equal, add to updated
+ if (!newEmail.equals(existingEmail)) {
+ updatedEmails.add(newEmail);
+ }
+
+ // remove from both
+ newEmailIterator.remove();
+ existingEmailIterator.remove();
+ }
+ }
+ }
+
+ // remaining emails in newEmail are inserts; remaining emails in existingEmail are deletes
+ accountEmailSqlDao.inTransaction(new Transaction<Void, AccountEmailSqlDao>() {
+ @Override
+ public Void inTransaction(AccountEmailSqlDao dao, TransactionStatus transactionStatus) throws Exception {
+ dao.create(emails, context);
+ dao.update(updatedEmails, context);
+ dao.delete(existingEmails, context);
+
+ List<String> insertHistoryIdList = getIdList(emails.size());
+ List<String> updateHistoryIdList = getIdList(updatedEmails.size());
+ List<String> deleteHistoryIdList = getIdList(existingEmails.size());
+
+ // insert histories
+ dao.insertAccountEmailHistoryFromTransaction(insertHistoryIdList, emails, ChangeType.INSERT, context);
+ dao.insertAccountEmailHistoryFromTransaction(updateHistoryIdList, updatedEmails, ChangeType.UPDATE, context);
+ dao.insertAccountEmailHistoryFromTransaction(deleteHistoryIdList, existingEmails, ChangeType.DELETE, context);
+
+ // insert audits
+ AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+ auditSqlDao.insertAuditFromTransaction(ACCOUNT_EMAIL_HISTORY_TABLE, insertHistoryIdList, ChangeType.INSERT, context);
+ auditSqlDao.insertAuditFromTransaction(ACCOUNT_EMAIL_HISTORY_TABLE, updateHistoryIdList, ChangeType.UPDATE, context);
+ auditSqlDao.insertAuditFromTransaction(ACCOUNT_EMAIL_HISTORY_TABLE, deleteHistoryIdList, ChangeType.DELETE, context);
+
+ return null;
+ }
+ });
+ }
+
+ @Override
public void test() {
accountSqlDao.test();
}
@@ -229,4 +290,6 @@ public class AuditedAccountDao implements AccountDao {
final CallContext context) {
customFieldDao.saveFields(transactionalDao, account.getId(), account.getObjectName(), account.getFieldList(), context);
}
+
+
}
diff --git a/account/src/main/resources/com/ning/billing/account/dao/AccountEmailSqlDao.sql.stg b/account/src/main/resources/com/ning/billing/account/dao/AccountEmailSqlDao.sql.stg
new file mode 100644
index 0000000..d287c08
--- /dev/null
+++ b/account/src/main/resources/com/ning/billing/account/dao/AccountEmailSqlDao.sql.stg
@@ -0,0 +1,48 @@
+group account_emails;
+
+fields(prefix) ::= <<
+ <prefix>id,
+ <prefix>account_id,
+ <prefix>email,
+ <prefix>created_by,
+ <prefix>created_date,
+ <prefix>updated_by,
+ <prefix>updated_date
+>>
+
+create() ::= <<
+ INSERT INTO account_emails(<fields()>)
+ VALUES
+ (:id, :accountId, :email, :userName, :createdDate, :userName, :updatedDate);
+>>
+
+update() ::= <<
+ UPDATE account_emails
+ SET email = :email, updated_by = :userName, updated_date = :updatedDate;
+>>
+
+delete() ::= <<
+ DELETE FROM account_emails
+ WHERE id = :id;
+>>
+
+insertAccountEmailHistoryFromTransaction() ::= <<
+ INSERT INTO account_email_history(history_record_id, id, account_id, email, change_type, updated_by, date)
+ VALUES (:historyRecordId, :id, :accountId, :email, :changeType, :userName, :updatedDate);
+>>
+
+getById() ::= <<
+ SELECT <fields()> FROM account_emails WHERE id = :id;
+>>
+
+get() ::= <<
+ SELECT <fields()> FROM account_emails;
+>>
+
+getByAccountId() ::= <<
+ SELECT <fields()> FROM account_emails WHERE account_id = :accountId;
+>>
+
+test() ::= <<
+ SELECT 1 FROM account_emails;
+>>
\ No newline at end of file
diff --git a/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg b/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
index eda3949..6bd5e79 100644
--- a/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
+++ b/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
@@ -19,6 +19,8 @@ accountFields(prefix) ::= <<
<prefix>country,
<prefix>postal_code,
<prefix>phone,
+ <prefix>migrated,
+ <prefix>is_notified_for_invoices,
<prefix>created_by,
<prefix>created_date,
<prefix>updated_by,
@@ -32,7 +34,7 @@ create() ::= <<
(:id, :externalKey, :email, :name, :firstNameLength, :currency, :billingCycleDay,
:paymentProviderName, :timeZone, :locale,
:address1, :address2, :companyName, :city, :stateOrProvince, :country, :postalCode, :phone,
- :userName, :createdDate, :userName, :updatedDate);
+ :migrated, :isNotifiedForInvoices, :userName, :createdDate, :userName, :updatedDate);
>>
update() ::= <<
@@ -42,10 +44,23 @@ update() ::= <<
time_zone = :timeZone, locale = :locale,
address1 = :address1, address2 = :address2, company_name = :companyName, city = :city, state_or_province = :stateOrProvince,
country = :country, postal_code = :postalCode, phone = :phone,
- updated_date = :updatedDate, updated_by = :userName
+ is_notified_for_invoices = :isNotifiedForInvoices, updated_date = :updatedDate, updated_by = :userName
WHERE id = :id;
>>
+insertAccountHistoryFromTransaction() ::= <<
+ INSERT INTO account_history
+ (history_record_id, id, external_key, email, name, first_name_length, currency,
+ billing_cycle_day, payment_provider_name, time_zone, locale,
+ address1, address2, company_name, city, state_or_province,
+ country, postal_code, phone, migrated, is_notified_for_invoices, change_type, updated_by, date)
+ VALUES
+ (:historyRecordId, :id, :externalKey, :email, :name, :firstNameLength, :currency,
+ :billingCycleDay, :paymentProviderName, :timeZone, :locale,
+ :address1, :address2, :companyName, :city, :stateOrProvince,
+ :country, :postalCode, :phone, :migrated, :isNotifiedForInvoices, :changeType, :userName, :createdDate);
+>>
+
getAccountByKey() ::= <<
select <accountFields()>
from accounts
diff --git a/account/src/main/resources/com/ning/billing/account/ddl.sql b/account/src/main/resources/com/ning/billing/account/ddl.sql
index ff9a41c..266215d 100644
--- a/account/src/main/resources/com/ning/billing/account/ddl.sql
+++ b/account/src/main/resources/com/ning/billing/account/ddl.sql
@@ -19,6 +19,7 @@ CREATE TABLE accounts (
postal_code varchar(11) DEFAULT NULL,
phone varchar(25) DEFAULT NULL,
migrated bool DEFAULT false,
+ is_notified_for_invoices boolean NOT NULL,
created_date datetime NOT NULL,
created_by varchar(50) NOT NULL,
updated_date datetime DEFAULT NULL,
@@ -30,7 +31,7 @@ CREATE UNIQUE INDEX accounts_email ON accounts(email);
DROP TABLE IF EXISTS account_history;
CREATE TABLE account_history (
- history_id char(36) NOT NULL,
+ history_record_id char(36) NOT NULL,
id char(36) NOT NULL,
external_key varchar(128) NULL,
email varchar(50) NOT NULL,
@@ -49,8 +50,35 @@ CREATE TABLE account_history (
country varchar(50) DEFAULT NULL,
postal_code varchar(11) DEFAULT NULL,
phone varchar(25) DEFAULT NULL,
+ migrated bool DEFAULT false,
+ is_notified_for_invoices boolean NOT NULL,
change_type char(6) NOT NULL,
updated_by varchar(50) NOT NULL,
- date datetime
+ date datetime NOT NULL
+) ENGINE=innodb;
+CREATE INDEX account_id ON account_history(id);
+
+DROP TABLE IF EXISTS account_emails;
+CREATE TABLE account_emails (
+ id char(36) NOT NULL,
+ account_id char(36) NOT NULL,
+ email varchar(50) NOT NULL,
+ created_by varchar(50) NOT NULL,
+ created_date datetime NOT NULL,
+ updated_by varchar(50) NOT NULL,
+ updated_date datetime NOT NULL,
+ PRIMARY KEY(id)
) ENGINE=innodb;
-CREATE INDEX account_id ON account_history(id);
\ No newline at end of file
+CREATE INDEX account_email_account_id ON account_emails(account_id);
+
+DROP TABLE IF EXISTS account_email_history;
+CREATE TABLE account_email_history (
+ history_record_id char(36) NOT NULL,
+ id char(36) NOT NULL,
+ account_id char(36) NOT NULL,
+ email varchar(50) NOT NULL,
+ change_type char(6) NOT NULL,
+ updated_by varchar(50) NOT NULL,
+ date datetime NOT NULL,
+ PRIMARY KEY(history_record_id)
+) ENGINE=innodb;
\ No newline at end of file
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 d2fea9e..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>();
@@ -53,14 +53,15 @@ public class MockAccountUserApi implements AccountUserApi {
Account result = new DefaultAccount(id, externalKey, email, name,
firstNameLength, currency, billCycleDay, paymentProviderName,
timeZone, locale, address1, address2, companyName, city,
- stateOrProvince, country, postalCode, phone, null, null, null, null);
+ stateOrProvince, country, postalCode, phone, false, false,
+ null, null, null, null);
accounts.add(result);
return result;
}
@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;
@@ -102,13 +103,23 @@ public class MockAccountUserApi implements AccountUserApi {
}
@Override
+ public List<AccountEmail> getEmails(UUID accountId) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void saveEmails(UUID accountId, List<AccountEmail> emails, CallContext context) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void updateAccount(final Account account, final CallContext context) {
throw new UnsupportedOperationException();
}
@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/api/user/TestEventJson.java b/account/src/test/java/com/ning/billing/account/api/user/TestEventJson.java
index d6f9f96..6518355 100644
--- a/account/src/test/java/com/ning/billing/account/api/user/TestEventJson.java
+++ b/account/src/test/java/com/ning/billing/account/api/user/TestEventJson.java
@@ -59,7 +59,7 @@ public class TestEventJson {
public void testAccountCreationEvent() throws Exception {
DefaultAccountData data = new DefaultAccountData("dsfdsf", "bobo", 3, "bobo@yahoo.com", 12, "USD", "paypal",
- "UTC", "US", "21 avenue", "", "Gling", "San Franciso", "CA", "94110", "USA", "4126789887");
+ "UTC", "US", "21 avenue", "", "Gling", "San Franciso", "CA", "94110", "USA", "4126789887", false, false);
DefaultAccountCreationEvent e = new DefaultAccountCreationEvent(data, UUID.randomUUID(), UUID.randomUUID());
String json = mapper.writeValueAsString(e);
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/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
index 38e2a6e..6c8b169 100644
--- a/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
@@ -24,7 +24,7 @@ import java.util.concurrent.ConcurrentHashMap;
import com.google.inject.Inject;
import com.ning.billing.account.api.Account;
-import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.account.api.AccountEmail;
import com.ning.billing.account.api.AccountChangeEvent;
import com.ning.billing.account.api.user.DefaultAccountChangeEvent;
import com.ning.billing.account.api.user.DefaultAccountCreationEvent;
@@ -84,6 +84,16 @@ public class MockAccountDao implements AccountDao {
}
@Override
+ public List<AccountEmail> getEmails(UUID accountId) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void saveEmails(UUID accountId, List<AccountEmail> emails, CallContext context) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void update(Account account, CallContext context) {
Account currentAccount = accounts.put(account.getId().toString(), account);
diff --git a/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
index ba48be8..ef7d265 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
@@ -16,6 +16,8 @@
package com.ning.billing.analytics;
+import com.ning.billing.util.email.EmailModule;
+import com.ning.billing.util.glue.GlobalLockerModule;
import org.skife.jdbi.v2.IDBI;
import com.ning.billing.account.glue.AccountModule;
@@ -42,6 +44,8 @@ public class AnalyticsTestModule extends AnalyticsModule
super.configure();
// Need to configure a few more things for the EventBus
+ install(new EmailModule());
+ install(new GlobalLockerModule());
install(new ClockModule());
install(new CallContextModule());
install(new FieldStoreModule());
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 3591582..a7091f8 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;
@@ -92,8 +93,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})
@@ -182,11 +181,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 0efe360..02dbb08 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;
@@ -62,6 +63,16 @@ public class MockAccount implements Account
}
@Override
+ public boolean isMigrated() {
+ return false;
+ }
+
+ @Override
+ public boolean isNotifiedForInvoices() {
+ return false;
+ }
+
+ @Override
public String getExternalKey()
{
return accountKey;
@@ -201,7 +212,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();
}
@@ -216,6 +232,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/MockDuration.java b/analytics/src/test/java/com/ning/billing/analytics/MockDuration.java
index f10ff2f..e2fa1a2 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockDuration.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockDuration.java
@@ -20,6 +20,7 @@ import com.ning.billing.catalog.api.Duration;
import com.ning.billing.catalog.api.TimeUnit;
import org.apache.commons.lang.NotImplementedException;
import org.joda.time.DateTime;
+import org.joda.time.Period;
public class MockDuration
{
@@ -43,6 +44,10 @@ public class MockDuration
public DateTime addToDateTime(DateTime dateTime) {
throw new NotImplementedException();
}
+ @Override
+ public Period toJodaPeriod() {
+ throw new UnsupportedOperationException();
+ }
};
}
@@ -66,6 +71,10 @@ public class MockDuration
public DateTime addToDateTime(DateTime dateTime) {
throw new NotImplementedException();
}
+ @Override
+ public Period toJodaPeriod() {
+ throw new UnsupportedOperationException();
+ }
};
}
@@ -89,6 +98,10 @@ public class MockDuration
public DateTime addToDateTime(DateTime dateTime) {
throw new NotImplementedException();
}
+ @Override
+ public Period toJodaPeriod() {
+ 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 8632938..cb786a0 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;
@@ -37,7 +38,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();
@@ -213,7 +213,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();
}
@@ -228,6 +233,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/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java b/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java
index e51711a..72aa146 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java
@@ -74,7 +74,7 @@ public class TestAnalyticsListener
@BeforeMethod(alwaysRun = true)
public void setUp() throws Exception
{
- final BusinessSubscriptionTransitionRecorder recorder = new BusinessSubscriptionTransitionRecorder(dao, catalogService, new MockEntitlementUserApi(bundleUUID, KEY), new MockIAccountUserApi(ACCOUNT_KEY, CURRENCY));
+ final BusinessSubscriptionTransitionRecorder recorder = new BusinessSubscriptionTransitionRecorder(dao, catalogService, new MockEntitlementUserApi(bundleUUID, KEY), new MockAccountUserApi(ACCOUNT_KEY, CURRENCY));
listener = new AnalyticsListener(recorder, null);
}
diff --git a/api/src/main/java/com/ning/billing/account/api/AccountData.java b/api/src/main/java/com/ning/billing/account/api/AccountData.java
index fa78b52..3220971 100644
--- a/api/src/main/java/com/ning/billing/account/api/AccountData.java
+++ b/api/src/main/java/com/ning/billing/account/api/AccountData.java
@@ -19,7 +19,6 @@ package com.ning.billing.account.api;
import org.joda.time.DateTimeZone;
import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.junction.api.BlockingState;
public interface AccountData {
@@ -57,4 +56,7 @@ public interface AccountData {
public String getPhone();
+ public boolean isMigrated();
+
+ public boolean isNotifiedForInvoices();
}
diff --git a/api/src/main/java/com/ning/billing/account/api/AccountEmail.java b/api/src/main/java/com/ning/billing/account/api/AccountEmail.java
new file mode 100644
index 0000000..88ec469
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/account/api/AccountEmail.java
@@ -0,0 +1,26 @@
+/*
+ * 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.account.api;
+
+import com.ning.billing.util.entity.UpdatableEntity;
+
+import java.util.UUID;
+
+public interface AccountEmail extends UpdatableEntity {
+ UUID getAccountId();
+ String getEmail();
+}
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 eabee0f..89e5f70 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;
@@ -51,4 +52,8 @@ public interface AccountUserApi {
public List<Account> getAccounts();
public UUID getIdFromKey(String externalKey) throws AccountApiException;
+
+ public List<AccountEmail> getEmails(UUID accountId);
+
+ public void saveEmails(UUID accountId, List<AccountEmail> emails, CallContext context);
}
diff --git a/api/src/main/java/com/ning/billing/account/api/MutableAccountData.java b/api/src/main/java/com/ning/billing/account/api/MutableAccountData.java
index 78dad92..6ea638d 100644
--- a/api/src/main/java/com/ning/billing/account/api/MutableAccountData.java
+++ b/api/src/main/java/com/ning/billing/account/api/MutableAccountData.java
@@ -21,41 +21,6 @@ import org.joda.time.DateTimeZone;
import com.ning.billing.catalog.api.Currency;
public interface MutableAccountData extends AccountData {
-
- public String getExternalKey();
-
- public String getEmail();
-
- public String getName();
-
- public int getFirstNameLength();
-
- public Currency getCurrency();
-
- public int getBillCycleDay();
-
- public String getPaymentProviderName();
-
- public DateTimeZone getTimeZone();
-
- public String getLocale();
-
- public String getAddress1();
-
- public String getAddress2();
-
- public String getCompanyName();
-
- public String getCity();
-
- public String getStateOrProvince();
-
- public String getCountry();
-
- public String getPostalCode();
-
- public String getPhone();
-
public void setExternalKey(String externalKey);
public void setEmail(String email);
@@ -90,4 +55,8 @@ public interface MutableAccountData extends AccountData {
public void setPhone(String phone);
+ public void setIsMigrated(boolean isMigrated);
+
+ public void setIsNotifiedForInvoices(boolean isNotifiedForInvoices);
+
}
\ No newline at end of file
diff --git a/api/src/main/java/com/ning/billing/catalog/api/Duration.java b/api/src/main/java/com/ning/billing/catalog/api/Duration.java
index 9025367..90ea5c2 100644
--- a/api/src/main/java/com/ning/billing/catalog/api/Duration.java
+++ b/api/src/main/java/com/ning/billing/catalog/api/Duration.java
@@ -17,6 +17,7 @@
package com.ning.billing.catalog.api;
import org.joda.time.DateTime;
+import org.joda.time.Period;
public interface Duration {
@@ -25,4 +26,6 @@ public interface Duration {
public abstract int getNumber();
public DateTime addToDateTime(DateTime dateTime);
+
+ public Period toJodaPeriod();
}
\ No newline at end of file
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/billing/BillingEvent.java b/api/src/main/java/com/ning/billing/entitlement/api/billing/BillingEvent.java
index 8681d59..eb8410f 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/billing/BillingEvent.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/billing/BillingEvent.java
@@ -113,5 +113,4 @@ public interface BillingEvent extends Comparable<BillingEvent> {
* @return a unique long indicating the ordering on which events got inserted on disk-- used for sorting only
*/
public Long getTotalOrdering();
-
}
diff --git a/api/src/main/java/com/ning/billing/ErrorCode.java b/api/src/main/java/com/ning/billing/ErrorCode.java
index dd22ea6..f6856eb 100644
--- a/api/src/main/java/com/ning/billing/ErrorCode.java
+++ b/api/src/main/java/com/ning/billing/ErrorCode.java
@@ -194,10 +194,17 @@ public enum ErrorCode {
*
*/
BLOCK_BLOCKED_ACTION(6000, "The action %s is block on this %s with id=%s"),
- BLOCK_TYPE_NOT_SUPPORTED(6001, "The Blockable type '%s' is not supported")
+ BLOCK_TYPE_NOT_SUPPORTED(6001, "The Blockable type '%s' is not supported"),
-
- ;
+ /*
+ *
+ * Range 9000: Miscellaneous
+ *
+ */
+ EMAIL_SENDING_FAILED(9000, "Sending email failed"),
+ EMAIL_PROPERTIES_FILE_MISSING(9001, "The properties file for email configuration could not be found."),
+ MISSING_TRANSLATION_RESOURCE(9010, "The resources for %s translation could not be found."),
+ MISSING_DEFAULT_TRANSLATION_RESOURCE(9011, "The default resource for %s translation could not be found.");
private int code;
diff --git a/api/src/main/java/com/ning/billing/invoice/api/formatters/InvoiceFormatterFactory.java b/api/src/main/java/com/ning/billing/invoice/api/formatters/InvoiceFormatterFactory.java
new file mode 100644
index 0000000..af4af92
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/invoice/api/formatters/InvoiceFormatterFactory.java
@@ -0,0 +1,26 @@
+/*
+ * 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.api.formatters;
+
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.util.template.translation.TranslatorConfig;
+
+import java.util.Locale;
+
+public interface InvoiceFormatterFactory {
+ public InvoiceFormatter createInvoiceFormatter(TranslatorConfig config, Invoice invoice, Locale locale);
+}
diff --git a/api/src/main/java/com/ning/billing/invoice/api/formatters/InvoiceItemFormatter.java b/api/src/main/java/com/ning/billing/invoice/api/formatters/InvoiceItemFormatter.java
new file mode 100644
index 0000000..c14e621
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/invoice/api/formatters/InvoiceItemFormatter.java
@@ -0,0 +1,24 @@
+/*
+ * 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.api.formatters;
+
+import com.ning.billing.invoice.api.InvoiceItem;
+
+public interface InvoiceItemFormatter extends InvoiceItem {
+ public String getFormattedStartDate();
+ public String getFormattedEndDate();
+}
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/email/EmailApiException.java b/api/src/main/java/com/ning/billing/util/email/EmailApiException.java
new file mode 100644
index 0000000..9b5d6ba
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/email/EmailApiException.java
@@ -0,0 +1,36 @@
+/*
+ * 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.util.email;
+
+import com.ning.billing.BillingExceptionBase;
+import com.ning.billing.ErrorCode;
+
+public class EmailApiException extends BillingExceptionBase {
+ private static final long serialVersionUID = 1L;
+
+ public EmailApiException(Throwable cause, int code, final String msg) {
+ super(cause, code, msg);
+ }
+
+ public EmailApiException(Throwable cause, ErrorCode code, final Object... args) {
+ super(cause, code, args);
+ }
+
+ public EmailApiException(ErrorCode code, final Object... args) {
+ super(code, args);
+ }
+}
diff --git a/api/src/main/java/com/ning/billing/util/email/EmailSender.java b/api/src/main/java/com/ning/billing/util/email/EmailSender.java
new file mode 100644
index 0000000..198a2eb
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/email/EmailSender.java
@@ -0,0 +1,24 @@
+/*
+ * 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.util.email;
+
+import java.io.IOException;
+import java.util.List;
+
+public interface EmailSender {
+ public void sendSecureEmail(List<String> to, List<String> cc, String subject, String htmlBody) throws IOException, EmailApiException;
+}
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..37d15bf 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 tagDefinition);
+ 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/api/src/main/java/com/ning/billing/util/template/translation/Translator.java b/api/src/main/java/com/ning/billing/util/template/translation/Translator.java
new file mode 100644
index 0000000..f50f4f6
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/template/translation/Translator.java
@@ -0,0 +1,23 @@
+/*
+ * 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.util.template.translation;
+
+import java.util.Locale;
+
+public interface Translator {
+ public String getTranslation(Locale locale, String originalText);
+}
diff --git a/api/src/main/java/com/ning/billing/util/template/translation/TranslatorConfig.java b/api/src/main/java/com/ning/billing/util/template/translation/TranslatorConfig.java
new file mode 100644
index 0000000..506c8bc
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/template/translation/TranslatorConfig.java
@@ -0,0 +1,26 @@
+/*
+ * 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.util.template.translation;
+
+import org.skife.config.Config;
+import org.skife.config.Default;
+
+public interface TranslatorConfig {
+ @Config("email.default.locale")
+ @Default("en_US")
+ public String getDefaultLocale();
+}
diff --git a/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/ServiceFinder.java b/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/ServiceFinder.java
index 83115de..e27dff9 100644
--- a/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/ServiceFinder.java
+++ b/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/ServiceFinder.java
@@ -60,7 +60,7 @@ public class ServiceFinder {
final Set<String> packageFilter = new HashSet<String>();
packageFilter.add("com.ning.billing");
final String jarFilter = "killbill";
- return findClasses(loader, KillbillService.class.getName().toString(), jarFilter, packageFilter);
+ return findClasses(loader, KillbillService.class.getName(), jarFilter, packageFilter);
} catch (ClassNotFoundException nfe) {
throw new RuntimeException("Failed to initialize ClassFinder", nfe);
}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/MockModule.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/MockModule.java
index 60434e1..4bae656 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/MockModule.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/MockModule.java
@@ -22,6 +22,8 @@ import java.io.IOException;
import java.net.URL;
import java.util.Set;
+import com.ning.billing.util.email.EmailConfig;
+import com.ning.billing.util.email.EmailModule;
import org.skife.config.ConfigurationObjectFactory;
import org.skife.jdbi.v2.IDBI;
@@ -83,6 +85,7 @@ public class MockModule extends AbstractModule {
bind(IDBI.class).toInstance(dbi);
}
+ install(new EmailModule());
install(new CallContextModule());
install(new GlobalLockerModule());
install(new BusModule());
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 c6d37bb..3e8dfb4 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
@@ -47,7 +47,6 @@ import com.ning.billing.invoice.api.Invoice;
@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);
@@ -96,8 +95,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());
@@ -147,10 +146,40 @@ public class TestIntegration extends TestIntegrationBase {
busHandler.pushExpectedEvent(NextEvent.PAYMENT);
clock.addDeltaFromReality(it.toDurationMillis());
assertTrue(busHandler.isCompleted(DELAY));
- }
-
+ }
- @Test(groups = "slow", enabled = true)
+ @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 {
DateTime initialDate = new DateTime(2012, 2, 1, 0, 3, 42, 0);
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
index 941df3c..a70bce9 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
@@ -292,6 +292,17 @@ public class TestIntegrationBase implements TestFailure {
public String getPhone() {
return "4152876341";
}
+
+ @Override
+ public boolean isMigrated() {
+ return false;
+ }
+
+ @Override
+ public boolean isNotifiedForInvoices() {
+ return false;
+ }
+
@Override
public String getExternalKey() {
return someRandomKey;
diff --git a/catalog/src/main/java/com/ning/billing/catalog/DefaultDuration.java b/catalog/src/main/java/com/ning/billing/catalog/DefaultDuration.java
index 0e821ef..71bfe50 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/DefaultDuration.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/DefaultDuration.java
@@ -21,6 +21,7 @@ import com.ning.billing.catalog.api.TimeUnit;
import com.ning.billing.util.config.ValidatingConfig;
import com.ning.billing.util.config.ValidationErrors;
import org.joda.time.DateTime;
+import org.joda.time.Period;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
@@ -52,7 +53,7 @@ public class DefaultDuration extends ValidatingConfig<StandaloneCatalog> impleme
@Override
public DateTime addToDateTime(DateTime dateTime) {
- if (number == null) {return dateTime;}
+ if ((number == null) && (unit != TimeUnit.UNLIMITED)) {return dateTime;}
switch (unit) {
case DAYS:
@@ -84,5 +85,21 @@ public class DefaultDuration extends ValidatingConfig<StandaloneCatalog> impleme
return this;
}
-
+ @Override
+ public Period toJodaPeriod() {
+ if ((number == null) && (unit != TimeUnit.UNLIMITED)) {return new Period();}
+
+ switch (unit) {
+ case DAYS:
+ return new Period().withDays(number);
+ case MONTHS:
+ return new Period().withMonths(number);
+ case YEARS:
+ return new Period().withYears(number);
+ case UNLIMITED:
+ return new Period().withYears(100);
+ default:
+ return new Period();
+ }
+ }
}
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 8943038..703e0bc 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;
@@ -55,8 +56,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);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
index e4c011a..e6d7705 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
@@ -31,6 +31,7 @@ import javax.annotation.Nullable;
import org.apache.commons.io.IOUtils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
+import org.joda.time.Period;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.annotations.AfterClass;
@@ -159,7 +160,7 @@ public abstract class TestApiBase {
return (! (theDao instanceof MockEntitlementDaoMemory));
}
- private void setupMySQL() throws IOException {
+ private void setupMySQL() throws IOException {
if (helper != null) {
final String entitlementDdl = IOUtils.toString(TestApiBase.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
final String utilDdl = IOUtils.toString(TestApiBase.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
@@ -254,7 +255,6 @@ public abstract class TestApiBase {
}
}
-
protected void assertDateWithin(DateTime in, DateTime lower, DateTime upper) {
assertTrue(in.isEqual(lower) || in.isAfter(lower));
assertTrue(in.isEqual(upper) || in.isBefore(upper));
@@ -275,6 +275,10 @@ public abstract class TestApiBase {
public DateTime addToDateTime(DateTime dateTime) {
return null;
}
+ @Override
+ public Period toJodaPeriod() {
+ throw new UnsupportedOperationException();
+ }
};
return result;
}
@@ -294,6 +298,10 @@ public abstract class TestApiBase {
public DateTime addToDateTime(DateTime dateTime) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
+ @Override
+ public Period toJodaPeriod() {
+ throw new UnsupportedOperationException();
+ }
};
return result;
}
@@ -314,6 +322,10 @@ public abstract class TestApiBase {
public DateTime addToDateTime(DateTime dateTime) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
+ @Override
+ public Period toJodaPeriod() {
+ throw new UnsupportedOperationException();
+ }
};
return result;
}
@@ -341,6 +353,16 @@ public abstract class TestApiBase {
}
@Override
+ public boolean isMigrated() {
+ return false;
+ }
+
+ @Override
+ public boolean isNotifiedForInvoices() {
+ return false;
+ }
+
+ @Override
public String getExternalKey() {
return "k123456";
}
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 df3d3f0..2780fe5 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
@@ -23,6 +23,7 @@ import static org.testng.Assert.assertTrue;
import org.apache.commons.lang.NotImplementedException;
import org.joda.time.DateTime;
+import org.joda.time.Period;
import org.testng.Assert;
import org.testng.annotations.Test;
@@ -84,9 +85,8 @@ public class TestUserApiAddOn extends TestApiBase {
}
}
- //TODO Martin re-enable this after merge
- @Test(enabled=false, groups={"slow"})
- public void testCancelBPWthAddon() {
+ @Test(enabled=true, groups={"slow"})
+ 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";
@@ -197,9 +197,8 @@ public class TestUserApiAddOn extends TestApiBase {
}
}
- //TODO Martin re-enable this after merge
- @Test(enabled=false, groups={"slow"})
- public void testChangeBPWthAddonNonAvailable() {
+ @Test(enabled=true, groups={"slow"})
+ public void testChangeBPWithAddonNonAvailable() {
try {
String baseProduct = "Shotgun";
@@ -364,20 +363,24 @@ public class TestUserApiAddOn extends TestApiBase {
//someTimeLater = aoCurrentPhase.getDuration();
someTimeLater = new Duration() {
- @Override
- public TimeUnit getUnit() {
+ @Override
+ public TimeUnit getUnit() {
return TimeUnit.DAYS;
- }
+ }
- @Override
- public int getNumber() {
+ @Override
+ public int getNumber() {
return 32;
- }
+ }
- @Override
- public DateTime addToDateTime(DateTime dateTime) {
+ @Override
+ public DateTime addToDateTime(DateTime dateTime) {
throw new NotImplementedException();
- }
+ }
+ @Override
+ public Period toJodaPeriod() {
+ throw new UnsupportedOperationException();
+ }
};
clock.addDeltaFromReality(someTimeLater);
invoice/pom.xml 7(+0 -7)
diff --git a/invoice/pom.xml b/invoice/pom.xml
index 552b41d..5f5522a 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -57,13 +57,6 @@
</dependency>
<dependency>
<groupId>com.ning.billing</groupId>
- <artifactId>killbill-junction</artifactId>
- <type>test-jar</type>
- <scope>test</scope>
- </dependency>
-
- <dependency>
- <groupId>com.ning.billing</groupId>
<artifactId>killbill-catalog</artifactId>
<scope>test</scope>
</dependency>
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
index caecf73..b4e4fd3 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
@@ -165,13 +165,13 @@ public class DefaultInvoiceDao implements InvoiceDao {
RecurringInvoiceItemSqlDao recurringInvoiceItemDao = invoiceDao.become(RecurringInvoiceItemSqlDao.class);
recurringInvoiceItemDao.batchCreateFromTransaction(recurringInvoiceItems, context);
- notifyOfFutureBillingEvents(invoiceSqlDao, recurringInvoiceItems);
+ notifyOfFutureBillingEvents(invoiceDao, recurringInvoiceItems);
List<InvoiceItem> fixedPriceInvoiceItems = invoice.getInvoiceItems(FixedPriceInvoiceItem.class);
FixedPriceInvoiceItemSqlDao fixedPriceInvoiceItemDao = invoiceDao.become(FixedPriceInvoiceItemSqlDao.class);
fixedPriceInvoiceItemDao.batchCreateFromTransaction(fixedPriceInvoiceItems, context);
- setChargedThroughDates(invoiceSqlDao, fixedPriceInvoiceItems, recurringInvoiceItems, context);
+ setChargedThroughDates(invoiceDao, fixedPriceInvoiceItems, recurringInvoiceItems, context);
// STEPH Why do we need that? Are the payments not always null at this point?
List<InvoicePayment> invoicePayments = invoice.getPayments();
diff --git a/invoice/src/main/java/com/ning/billing/invoice/glue/DefaultInvoiceModule.java b/invoice/src/main/java/com/ning/billing/invoice/glue/DefaultInvoiceModule.java
index b825d81..128e8fa 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/glue/DefaultInvoiceModule.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/glue/DefaultInvoiceModule.java
@@ -23,9 +23,11 @@ import com.ning.billing.config.InvoiceConfig;
import com.ning.billing.invoice.InvoiceListener;
import com.ning.billing.invoice.api.DefaultInvoiceService;
import com.ning.billing.invoice.api.InvoiceMigrationApi;
+import com.ning.billing.invoice.api.InvoiceNotifier;
import com.ning.billing.invoice.api.InvoicePaymentApi;
import com.ning.billing.invoice.api.InvoiceService;
import com.ning.billing.invoice.api.InvoiceUserApi;
+import com.ning.billing.invoice.api.formatters.InvoiceFormatterFactory;
import com.ning.billing.invoice.api.invoice.DefaultInvoicePaymentApi;
import com.ning.billing.invoice.api.migration.DefaultInvoiceMigrationApi;
import com.ning.billing.invoice.api.user.DefaultInvoiceUserApi;
@@ -37,8 +39,11 @@ import com.ning.billing.invoice.notification.DefaultNextBillingDateNotifier;
import com.ning.billing.invoice.notification.DefaultNextBillingDatePoster;
import com.ning.billing.invoice.notification.NextBillingDateNotifier;
import com.ning.billing.invoice.notification.NextBillingDatePoster;
-import com.ning.billing.util.glue.GlobalLockerModule;
-
+import com.ning.billing.invoice.notification.NullInvoiceNotifier;
+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;
public class DefaultInvoiceModule extends AbstractModule {
protected void installInvoiceDao() {
@@ -66,28 +71,34 @@ public class DefaultInvoiceModule extends AbstractModule {
bind(InvoiceMigrationApi.class).to(DefaultInvoiceMigrationApi.class).asEagerSingleton();
}
- protected void installNotifier() {
+ 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(NullInvoiceNotifier.class).asEagerSingleton();
}
protected void installInvoiceListener() {
bind(InvoiceListener.class).asEagerSingleton();
}
+ protected void installInvoiceGenerator() {
+ bind(InvoiceGenerator.class).to(DefaultInvoiceGenerator.class).asEagerSingleton();
+ }
+
@Override
protected void configure() {
installInvoiceService();
- installNotifier();
-
- installInvoiceListener();
- bind(InvoiceGenerator.class).to(DefaultInvoiceGenerator.class).asEagerSingleton();
installConfig();
+ installNotifiers();
+ installInvoiceListener();
+ installInvoiceGenerator();
installInvoiceDao();
installInvoiceUserApi();
installInvoicePaymentApi();
installInvoiceMigrationApi();
}
-
-
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
index 7de8c71..53093a2 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
@@ -34,6 +34,7 @@ import com.ning.billing.catalog.api.Currency;
import com.ning.billing.entitlement.api.billing.BillingEvent;
import com.ning.billing.entitlement.api.user.SubscriptionEvent;
import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceNotifier;
import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.invoice.api.InvoiceItem;
import com.ning.billing.invoice.api.user.DefaultEmptyInvoiceEvent;
@@ -59,6 +60,7 @@ public class InvoiceDispatcher {
private final BillingApi billingApi;
private final AccountUserApi accountUserApi;
private final InvoiceDao invoiceDao;
+ private final InvoiceNotifier invoiceNotifier;
private final GlobalLocker locker;
private final Bus eventBus;
private final Clock clock;
@@ -69,6 +71,7 @@ public class InvoiceDispatcher {
public InvoiceDispatcher(final InvoiceGenerator generator, final AccountUserApi accountUserApi,
final BillingApi billingApi,
final InvoiceDao invoiceDao,
+ final InvoiceNotifier invoiceNotifier,
final GlobalLocker locker,
final Bus eventBus,
final Clock clock) {
@@ -76,6 +79,7 @@ public class InvoiceDispatcher {
this.billingApi = billingApi;
this.accountUserApi = accountUserApi;
this.invoiceDao = invoiceDao;
+ this.invoiceNotifier = invoiceNotifier;
this.locker = locker;
this.eventBus = eventBus;
this.clock = clock;
@@ -138,6 +142,7 @@ public class InvoiceDispatcher {
log.error("Failed to post DefaultEmptyInvoiceNotification event for account {} ", accountId, e);
}
}
+
private Invoice processAccountWithLock(final UUID accountId, final DateTime targetDate,
final boolean dryRun, final CallContext context) throws InvoiceApiException {
try {
@@ -169,13 +174,16 @@ public class InvoiceDispatcher {
invoiceDao.create(invoice, context);
}
}
+
+ if (account.isNotifiedForInvoices()) {
+ invoiceNotifier.notify(account, invoice);
+ }
+
return invoice;
} catch(AccountApiException e) {
log.error("Failed handling entitlement change.",e);
return null;
-
}
-
}
private void outputDebugData(Collection<BillingEvent> events, Collection<Invoice> invoices) {
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..c24215c 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,92 @@ 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
+ if (invoices != null) {
+ 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/notification/EmailInvoiceNotifier.java b/invoice/src/main/java/com/ning/billing/invoice/notification/EmailInvoiceNotifier.java
new file mode 100644
index 0000000..58026b2
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/notification/EmailInvoiceNotifier.java
@@ -0,0 +1,79 @@
+/*
+ * 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.notification;
+
+import com.google.inject.Inject;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountEmail;
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.api.InvoiceNotifier;
+import com.ning.billing.invoice.template.HtmlInvoiceGenerator;
+import com.ning.billing.util.email.DefaultEmailSender;
+import com.ning.billing.util.email.EmailApiException;
+import com.ning.billing.util.email.EmailConfig;
+import com.ning.billing.util.email.EmailSender;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class EmailInvoiceNotifier implements InvoiceNotifier {
+ private final AccountUserApi accountUserApi;
+ private final HtmlInvoiceGenerator generator;
+ private final EmailConfig config;
+
+ @Inject
+ public EmailInvoiceNotifier(AccountUserApi accountUserApi, HtmlInvoiceGenerator generator, EmailConfig config) {
+ this.accountUserApi = accountUserApi;
+ this.generator = generator;
+ this.config = config;
+ }
+
+ @Override
+ public void notify(Account account, Invoice invoice) throws InvoiceApiException {
+ List<String> to = new ArrayList<String>();
+ to.add(account.getEmail());
+
+ List<AccountEmail> accountEmailList = accountUserApi.getEmails(account.getId());
+ List<String> cc = new ArrayList<String>();
+ for (AccountEmail email : accountEmailList) {
+ cc.add(email.getEmail());
+ }
+
+ String htmlBody = null;
+ try {
+ htmlBody = generator.generateInvoice(account, invoice, "HtmlInvoiceTemplate");
+ } catch (IOException e) {
+ throw new InvoiceApiException(e, ErrorCode.EMAIL_SENDING_FAILED);
+ }
+
+ // TODO: get subject
+ String subject = "";
+
+ EmailSender sender = new DefaultEmailSender(config);
+ try {
+ sender.sendSecureEmail(to, cc, subject, htmlBody);
+ } catch (EmailApiException e) {
+ throw new InvoiceApiException(e, ErrorCode.EMAIL_SENDING_FAILED);
+ } catch (IOException e) {
+ throw new InvoiceApiException(e, ErrorCode.EMAIL_SENDING_FAILED);
+ }
+ }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/notification/NullInvoiceNotifier.java b/invoice/src/main/java/com/ning/billing/invoice/notification/NullInvoiceNotifier.java
new file mode 100644
index 0000000..460e779
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/notification/NullInvoiceNotifier.java
@@ -0,0 +1,28 @@
+/*
+ * 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.notification;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceNotifier;
+
+public class NullInvoiceNotifier implements InvoiceNotifier {
+ @Override
+ public void notify(Account account, Invoice invoice) {
+ // deliberate no-op
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..2fad805
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
@@ -0,0 +1,286 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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.template.formatters;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
+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;
+import org.joda.time.DateTime;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.UUID;
+
+public class DefaultInvoiceFormatter implements InvoiceFormatter {
+ private final TranslatorConfig config;
+ private final Invoice invoice;
+ private final DateTimeFormatter dateFormatter;
+ private final Locale locale;
+
+ public DefaultInvoiceFormatter(TranslatorConfig config, Invoice invoice, Locale locale) {
+ this.config = config;
+ this.invoice = invoice;
+ dateFormatter = DateTimeFormat.mediumDate().withLocale(locale);
+ this.locale = locale;
+ }
+
+ @Override
+ public Integer getInvoiceNumber() {
+ return invoice.getInvoiceNumber();
+ }
+
+ @Override
+ public List<InvoiceItem> getInvoiceItems() {
+ List<InvoiceItem> formatters = new ArrayList<InvoiceItem>();
+ for (InvoiceItem item : invoice.getInvoiceItems()) {
+ formatters.add(new DefaultInvoiceItemFormatter(config, item, dateFormatter, locale));
+ }
+ return formatters;
+ }
+
+ @Override
+ public boolean addInvoiceItem(InvoiceItem item) {
+ return invoice.addInvoiceItem(item);
+ }
+
+ @Override
+ public boolean addInvoiceItems(List<InvoiceItem> items) {
+ return invoice.addInvoiceItems(items);
+ }
+
+ @Override
+ public <T extends InvoiceItem> List<InvoiceItem> getInvoiceItems(Class<T> clazz) {
+ return invoice.getInvoiceItems(clazz);
+ }
+
+ @Override
+ public int getNumberOfItems() {
+ return invoice.getNumberOfItems();
+ }
+
+ @Override
+ public boolean addPayment(InvoicePayment payment) {
+ return invoice.addPayment(payment);
+ }
+
+ @Override
+ public boolean addPayments(List<InvoicePayment> payments) {
+ return invoice.addPayments(payments);
+ }
+
+ @Override
+ public List<InvoicePayment> getPayments() {
+ return invoice.getPayments();
+ }
+
+ @Override
+ public int getNumberOfPayments() {
+ return invoice.getNumberOfPayments();
+ }
+
+ @Override
+ public UUID getAccountId() {
+ return invoice.getAccountId();
+ }
+
+ @Override
+ public BigDecimal getTotalAmount() {
+ return invoice.getTotalAmount();
+ }
+
+ @Override
+ public BigDecimal getBalance() {
+ return invoice.getBalance();
+ }
+
+ @Override
+ public boolean isDueForPayment(DateTime targetDate, int numberOfDays) {
+ return invoice.isDueForPayment(targetDate, numberOfDays);
+ }
+
+ @Override
+ public boolean isMigrationInvoice() {
+ return invoice.isMigrationInvoice();
+ }
+
+ @Override
+ public DateTime getInvoiceDate() {
+ return invoice.getInvoiceDate();
+ }
+
+ @Override
+ public DateTime getTargetDate() {
+ return invoice.getTargetDate();
+ }
+
+ @Override
+ public Currency getCurrency() {
+ return invoice.getCurrency();
+ }
+
+ @Override
+ public DateTime getLastPaymentAttempt() {
+ return invoice.getLastPaymentAttempt();
+ }
+
+ @Override
+ public BigDecimal getAmountPaid() {
+ return invoice.getAmountPaid();
+ }
+
+ @Override
+ public String getFormattedInvoiceDate() {
+ return invoice.getInvoiceDate().toString(dateFormatter);
+ }
+
+ @Override
+ public String getFieldValue(String fieldName) {
+ return invoice.getFieldValue(fieldName);
+ }
+
+ @Override
+ public void setFieldValue(String fieldName, String fieldValue) {
+ invoice.setFieldValue(fieldName, fieldValue);
+ }
+
+ @Override
+ public void saveFieldValue(String fieldName, String fieldValue, CallContext context) {
+ invoice.saveFieldValue(fieldName, fieldValue, context);
+ }
+
+ @Override
+ public List<CustomField> getFieldList() {
+ return invoice.getFieldList();
+ }
+
+ @Override
+ public void setFields(List<CustomField> fields) {
+ invoice.setFields(fields);
+ }
+
+ @Override
+ public void saveFields(List<CustomField> fields, CallContext context) {
+ invoice.saveFields(fields, context);
+ }
+
+ @Override
+ public void clearFields() {
+ invoice.clearFields();
+ }
+
+ @Override
+ public void clearPersistedFields(CallContext context) {
+ invoice.clearPersistedFields(context);
+ }
+
+ @Override
+ public String getObjectName() {
+ return invoice.getObjectName();
+ }
+
+ @Override
+ public UUID getId() {
+ return invoice.getId();
+ }
+
+ @Override
+ public String getCreatedBy() {
+ return invoice.getCreatedBy();
+ }
+
+ @Override
+ public DateTime getCreatedDate() {
+ return invoice.getCreatedDate();
+ }
+
+ @Override
+ public List<Tag> getTagList() {
+ return invoice.getTagList();
+ }
+
+ @Override
+ public boolean hasTag(TagDefinition tagDefinition) {
+ return invoice.hasTag(tagDefinition);
+ }
+
+ @Override
+ public boolean hasTag(ControlTagType controlTagType) {
+ return invoice.hasTag(controlTagType);
+ }
+
+ @Override
+ public void addTag(TagDefinition definition) {
+ invoice.addTag(definition);
+ }
+
+ @Override
+ public void addTags(List<Tag> tags) {
+ invoice.addTags(tags);
+ }
+
+ @Override
+ public void addTagsFromDefinitions(List<TagDefinition> tagDefinitions) {
+ invoice.addTagsFromDefinitions(tagDefinitions);
+ }
+
+ @Override
+ public void clearTags() {
+ invoice.clearTags();
+ }
+
+ @Override
+ public void removeTag(TagDefinition definition) {
+ invoice.removeTag(definition);
+ }
+
+ @Override
+ public boolean generateInvoice() {
+ return invoice.generateInvoice();
+ }
+
+ @Override
+ public boolean processPayment() {
+ return invoice.processPayment();
+ }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatterFactory.java b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatterFactory.java
new file mode 100644
index 0000000..83c0619
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatterFactory.java
@@ -0,0 +1,31 @@
+/*
+ * 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.template.formatters;
+
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.formatters.InvoiceFormatter;
+import com.ning.billing.invoice.api.formatters.InvoiceFormatterFactory;
+import com.ning.billing.util.template.translation.TranslatorConfig;
+
+import java.util.Locale;
+
+public class DefaultInvoiceFormatterFactory implements InvoiceFormatterFactory {
+ @Override
+ public InvoiceFormatter createInvoiceFormatter(TranslatorConfig config, Invoice invoice, Locale locale) {
+ return new DefaultInvoiceFormatter(config, invoice, locale);
+ }
+}
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
new file mode 100644
index 0000000..cc072d9
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
@@ -0,0 +1,136 @@
+/*
+ * 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.template.formatters;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.formatters.InvoiceItemFormatter;
+import com.ning.billing.util.template.translation.DefaultCatalogTranslator;
+import com.ning.billing.util.template.translation.Translator;
+import com.ning.billing.util.template.translation.TranslatorConfig;
+import org.joda.time.DateTime;
+import org.joda.time.format.DateTimeFormatter;
+
+import java.math.BigDecimal;
+import java.util.Locale;
+import java.util.UUID;
+
+public class DefaultInvoiceItemFormatter implements InvoiceItemFormatter {
+ private final Translator translator;
+
+ private final InvoiceItem item;
+ private final DateTimeFormatter dateFormatter;
+ private final Locale locale;
+
+ public DefaultInvoiceItemFormatter(TranslatorConfig config, InvoiceItem item, DateTimeFormatter dateFormatter, Locale locale) {
+ this.item = item;
+ this.dateFormatter = dateFormatter;
+ this.locale = locale;
+
+ this.translator = new DefaultCatalogTranslator(config);
+ }
+
+ @Override
+ public BigDecimal getAmount() {
+ return item.getAmount();
+ }
+
+ @Override
+ public Currency getCurrency() {
+ return item.getCurrency();
+ }
+
+ @Override
+ public InvoiceItem asReversingItem() {
+ return item.asReversingItem();
+ }
+
+ @Override
+ public String getDescription() {
+ return item.getDescription();
+ }
+
+ @Override
+ public DateTime getStartDate() {
+ return item.getStartDate();
+ }
+
+ @Override
+ public DateTime getEndDate() {
+ return item.getEndDate();
+ }
+
+ @Override
+ public String getFormattedStartDate() {
+ return item.getStartDate().toString(dateFormatter);
+ }
+
+ @Override
+ public String getFormattedEndDate() {
+ return item.getEndDate().toString(dateFormatter);
+ }
+
+ @Override
+ public UUID getInvoiceId() {
+ return item.getInvoiceId();
+ }
+
+ @Override
+ public UUID getAccountId() {
+ return item.getAccountId();
+ }
+
+ @Override
+ public UUID getBundleId() {
+ return item.getBundleId();
+ }
+
+ @Override
+ public UUID getSubscriptionId() {
+ return item.getSubscriptionId();
+ }
+
+ @Override
+ public String getPlanName() {
+ return translator.getTranslation(locale, item.getPlanName());
+ }
+
+ @Override
+ public String getPhaseName() {
+ return translator.getTranslation(locale, item.getPhaseName());
+ }
+
+ @Override
+ public int compareTo(InvoiceItem invoiceItem) {
+ return item.compareTo(invoiceItem);
+ }
+
+ @Override
+ public UUID getId() {
+ return item.getId();
+ }
+
+ @Override
+ public String getCreatedBy() {
+ return item.getCreatedBy();
+ }
+
+ @Override
+ public DateTime getCreatedDate() {
+ return item.getCreatedDate();
+ }
+}
\ No newline at end of file
diff --git a/invoice/src/main/java/com/ning/billing/invoice/template/HtmlInvoiceGenerator.java b/invoice/src/main/java/com/ning/billing/invoice/template/HtmlInvoiceGenerator.java
new file mode 100644
index 0000000..2a006d8
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/template/HtmlInvoiceGenerator.java
@@ -0,0 +1,59 @@
+/*
+ * 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.template;
+
+import com.google.inject.Inject;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.formatters.InvoiceFormatter;
+import com.ning.billing.invoice.api.formatters.InvoiceFormatterFactory;
+import com.ning.billing.invoice.template.translator.DefaultInvoiceTranslator;
+import com.ning.billing.util.email.templates.TemplateEngine;
+import com.ning.billing.util.template.translation.TranslatorConfig;
+
+import java.io.IOException;
+import java.lang.String;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+public class HtmlInvoiceGenerator {
+ private final InvoiceFormatterFactory factory;
+ private final TemplateEngine templateEngine;
+ private final TranslatorConfig config;
+
+ @Inject
+ public HtmlInvoiceGenerator(InvoiceFormatterFactory factory, TemplateEngine templateEngine, TranslatorConfig config) {
+ this.factory = factory;
+ this.templateEngine = templateEngine;
+ this.config = config;
+ }
+
+ public String generateInvoice(Account account, Invoice invoice, String templateName) throws IOException {
+ Map<String, Object> data = new HashMap<String, Object>();
+ DefaultInvoiceTranslator invoiceTranslator = new DefaultInvoiceTranslator(config);
+ Locale locale = new Locale(account.getLocale());
+ invoiceTranslator.setLocale(locale);
+ data.put("text", invoiceTranslator);
+ data.put("account", account);
+
+ InvoiceFormatter formattedInvoice = factory.createInvoiceFormatter(config, invoice, locale);
+ data.put("invoice", formattedInvoice);
+
+ return templateEngine.executeTemplate(templateName, data);
+ }
+}
\ No newline at end of file
diff --git a/invoice/src/main/java/com/ning/billing/invoice/template/translator/DefaultInvoiceTranslator.java b/invoice/src/main/java/com/ning/billing/invoice/template/translator/DefaultInvoiceTranslator.java
new file mode 100644
index 0000000..f238ea7
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/template/translator/DefaultInvoiceTranslator.java
@@ -0,0 +1,136 @@
+/*
+ * 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.template.translator;
+
+import com.google.inject.Inject;
+import com.ning.billing.util.template.translation.DefaultTranslatorBase;
+import com.ning.billing.util.template.translation.TranslatorConfig;
+
+import java.util.Locale;
+
+public class DefaultInvoiceTranslator extends DefaultTranslatorBase implements InvoiceStrings {
+ private Locale locale;
+
+ @Inject
+ public DefaultInvoiceTranslator(TranslatorConfig config) {
+ super(config);
+ }
+
+ public void setLocale(Locale locale) {
+ this.locale = locale;
+ }
+
+ @Override
+ protected String getBundlePath() {
+ return "com/ning/billing/util/email/translation/InvoiceTranslation";
+ }
+
+ @Override
+ protected String getTranslationType() {
+ return "invoice";
+ }
+
+ @Override
+ public String getInvoiceTitle() {
+ return getTranslation(locale, "invoiceTitle");
+ }
+
+ @Override
+ public String getInvoiceDate() {
+ return getTranslation(locale, "invoiceDate");
+ }
+
+ @Override
+ public String getInvoiceNumber() {
+ return getTranslation(locale, "invoiceNumber");
+ }
+
+ @Override
+ public String getAccountOwnerName() {
+ return getTranslation(locale, "accountOwnerName");
+ }
+
+ @Override
+ public String getAccountOwnerEmail() {
+ return getTranslation(locale, "accountOwnerEmail");
+ }
+
+ @Override
+ public String getAccountOwnerPhone() {
+ return getTranslation(locale, "accountOwnerPhone");
+ }
+
+ @Override
+ public String getCompanyName() {
+ return getTranslation(locale, "companyName");
+ }
+
+ @Override
+ public String getCompanyAddress() {
+ return getTranslation(locale, "companyAddress");
+ }
+
+ @Override
+ public String getCompanyCityProvincePostalCode() {
+ return getTranslation(locale, "");
+ }
+
+ @Override
+ public String getCompanyCountry() {
+ return getTranslation(locale, "companyCountry");
+ }
+
+ @Override
+ public String getCompanyUrl() {
+ return getTranslation(locale, "companyUrl");
+ }
+
+ @Override
+ public String getInvoiceItemBundleName() {
+ return getTranslation(locale, "invoiceItemBundleName");
+ }
+
+ @Override
+ public String getInvoiceItemDescription() {
+ return getTranslation(locale, "invoiceItemDescription");
+ }
+
+ @Override
+ public String getInvoiceItemServicePeriod() {
+ return getTranslation(locale, "invoiceItemServicePeriod");
+ }
+
+ @Override
+ public String getInvoiceItemAmount() {
+ return getTranslation(locale, "invoiceItemAmount");
+ }
+
+ @Override
+ public String getInvoiceAmount() {
+ return getTranslation(locale, "invoiceAmount");
+ }
+
+ @Override
+ public String getInvoiceAmountPaid() {
+ return getTranslation(locale, "invoiceAmountPaid");
+ }
+
+ @Override
+ public String getInvoiceBalance() {
+ return getTranslation(locale, "invoiceBalance");
+ }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/template/translator/InvoiceStrings.java b/invoice/src/main/java/com/ning/billing/invoice/template/translator/InvoiceStrings.java
new file mode 100644
index 0000000..cc19d5f
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/template/translator/InvoiceStrings.java
@@ -0,0 +1,42 @@
+/*
+ * 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.template.translator;
+
+public interface InvoiceStrings {
+ String getInvoiceTitle();
+ String getInvoiceDate();
+ String getInvoiceNumber();
+ String getAccountOwnerName();
+ String getAccountOwnerEmail();
+ String getAccountOwnerPhone();
+
+ // company name and address
+ String getCompanyName();
+ String getCompanyAddress();
+ String getCompanyCityProvincePostalCode();
+ String getCompanyCountry();
+ String getCompanyUrl();
+
+ String getInvoiceItemBundleName();
+ String getInvoiceItemDescription();
+ String getInvoiceItemServicePeriod();
+ String getInvoiceItemAmount();
+
+ String getInvoiceAmount();
+ String getInvoiceAmountPaid();
+ String getInvoiceBalance();
+}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/migration/MockModuleNoEntitlement.java b/invoice/src/test/java/com/ning/billing/invoice/api/migration/MockModuleNoEntitlement.java
index 7b3423c..2dcd4d9 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/migration/MockModuleNoEntitlement.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/migration/MockModuleNoEntitlement.java
@@ -16,39 +16,39 @@
package com.ning.billing.invoice.api.migration;
-import com.ning.billing.entitlement.api.billing.ChargeThruApi;
import com.ning.billing.invoice.MockModule;
+import com.ning.billing.invoice.api.InvoiceNotifier;
import com.ning.billing.invoice.glue.DefaultInvoiceModule;
import com.ning.billing.invoice.notification.NextBillingDateNotifier;
import com.ning.billing.invoice.notification.NextBillingDatePoster;
-import com.ning.billing.junction.api.BillingApi;
+import com.ning.billing.invoice.notification.NullInvoiceNotifier;
import com.ning.billing.mock.BrainDeadProxyFactory;
import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
public class MockModuleNoEntitlement extends MockModule {
-
- @Override
- protected void installEntitlementModule() {
- BillingApi entitlementApi = BrainDeadProxyFactory.createBrainDeadProxyFor(BillingApi.class);
- ((ZombieControl)entitlementApi).addResult("setChargedThroughDateFromTransaction", BrainDeadProxyFactory.ZOMBIE_VOID);
- ((ZombieControl)entitlementApi).addResult("getBillingEventsForAccountAndUpdateAccountBCD", BrainDeadProxyFactory.ZOMBIE_VOID);
- //bind(BillingApi.class).toInstance(entitlementApi);
-// bind(EntitlementUserApi.class).toInstance(BrainDeadProxyFactory.createBrainDeadProxyFor(EntitlementUserApi.class));
- ChargeThruApi cta = BrainDeadProxyFactory.createBrainDeadProxyFor(ChargeThruApi.class);
- bind(ChargeThruApi.class).toInstance(cta);
- ((ZombieControl)cta).addResult("setChargedThroughDateFromTransaction", BrainDeadProxyFactory.ZOMBIE_VOID);
- }
+// @Override
+// protected void installEntitlementModule() {
+// BillingApi entitlementApi = BrainDeadProxyFactory.createBrainDeadProxyFor(BillingApi.class);
+// ((ZombieControl)entitlementApi).addResult("setChargedThroughDateFromTransaction", BrainDeadProxyFactory.ZOMBIE_VOID);
+// ((ZombieControl)entitlementApi).addResult("getBillingEventsForAccountAndUpdateAccountBCD", BrainDeadProxyFactory.ZOMBIE_VOID);
+// //bind(BillingApi.class).toInstance(entitlementApi);
+//// bind(EntitlementUserApi.class).toInstance(BrainDeadProxyFactory.createBrainDeadProxyFor(EntitlementUserApi.class));
+// ChargeThruApi cta = BrainDeadProxyFactory.createBrainDeadProxyFor(ChargeThruApi.class);
+// bind(ChargeThruApi.class).toInstance(cta);
+// ((ZombieControl)cta).addResult("setChargedThroughDateFromTransaction", BrainDeadProxyFactory.ZOMBIE_VOID);
+// }
@Override
protected void installInvoiceModule() {
install(new DefaultInvoiceModule(){
@Override
- protected void installNotifier() {
+ protected void installNotifiers() {
bind(NextBillingDateNotifier.class).toInstance(BrainDeadProxyFactory.createBrainDeadProxyFor(NextBillingDateNotifier.class));
NextBillingDatePoster poster = BrainDeadProxyFactory.createBrainDeadProxyFor(NextBillingDatePoster.class);
((ZombieControl)poster).addResult("insertNextBillingNotification",BrainDeadProxyFactory.ZOMBIE_VOID);
bind(NextBillingDatePoster.class).toInstance(poster);
+ bind(InvoiceNotifier.class).to(NullInvoiceNotifier.class).asEagerSingleton();
}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
index 8efba0d..5d7d14f 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
@@ -23,6 +23,7 @@ import java.util.SortedSet;
import java.util.TreeSet;
import java.util.UUID;
+import com.ning.billing.invoice.tests.InvoicingTestBase;
import org.apache.commons.io.IOUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
@@ -51,12 +52,13 @@ import com.ning.billing.invoice.InvoiceDispatcher;
import com.ning.billing.invoice.TestInvoiceDispatcher;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceMigrationApi;
+import com.ning.billing.invoice.api.InvoiceNotifier;
import com.ning.billing.invoice.api.InvoicePaymentApi;
import com.ning.billing.invoice.api.InvoiceUserApi;
import com.ning.billing.invoice.dao.InvoiceDao;
import com.ning.billing.invoice.model.InvoiceGenerator;
+import com.ning.billing.invoice.notification.NullInvoiceNotifier;
import com.ning.billing.junction.api.BillingApi;
-import com.ning.billing.junction.plumbing.billing.DefaultBillingEvent;
import com.ning.billing.mock.BrainDeadProxyFactory;
import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
import com.ning.billing.util.bus.BusService;
@@ -69,8 +71,8 @@ import com.ning.billing.util.clock.Clock;
import com.ning.billing.util.clock.ClockMock;
import com.ning.billing.util.globallocker.GlobalLocker;
-@Guice(modules = {MockModuleNoEntitlement.class, })
-public class TestDefaultInvoiceMigrationApi {
+@Guice(modules = {MockModuleNoEntitlement.class})
+public class TestDefaultInvoiceMigrationApi extends InvoicingTestBase {
Logger log = LoggerFactory.getLogger(TestDefaultInvoiceMigrationApi.class);
@Inject
@@ -98,8 +100,6 @@ public class TestDefaultInvoiceMigrationApi {
@Inject
private BillingApi billingApi;
-
-
private UUID accountId ;
private UUID subscriptionId ;
private DateTime date_migrated;
@@ -174,6 +174,7 @@ public class TestDefaultInvoiceMigrationApi {
((ZombieControl)accountUserApi).addResult("getAccountById", account);
((ZombieControl)account).addResult("getCurrency", Currency.USD);
((ZombieControl)account).addResult("getId", accountId);
+ ((ZombieControl)account).addResult("isNotifiedForInvoices", true);
Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
((ZombieControl)subscription).addResult("getId", subscriptionId);
@@ -184,13 +185,16 @@ public class TestDefaultInvoiceMigrationApi {
DateTime effectiveDate = new DateTime().minusDays(1);
Currency currency = Currency.USD;
BigDecimal fixedPrice = null;
- events.add(new DefaultBillingEvent(account, subscription, effectiveDate,plan, planPhase,
- fixedPrice, BigDecimal.ONE, currency, BillingPeriod.MONTHLY, 1,
- BillingModeType.IN_ADVANCE, "", 1L, SubscriptionTransitionType.CREATE));
+ events.add(createMockBillingEvent(account, subscription, effectiveDate, plan, planPhase,
+ fixedPrice, BigDecimal.ONE, currency, BillingPeriod.MONTHLY, 1,
+ BillingModeType.IN_ADVANCE, "", 1L, SubscriptionTransitionType.CREATE));
BillingApi entitlementBillingApi = BrainDeadProxyFactory.createBrainDeadProxyFor(BillingApi.class);
((ZombieControl)entitlementBillingApi).addResult("getBillingEventsForAccountAndUpdateAccountBCD", events);
- InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountUserApi, entitlementBillingApi, invoiceDao, locker, busService.getBus(), clock);
+
+ InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
+ InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountUserApi, entitlementBillingApi,
+ invoiceDao, invoiceNotifier, locker, busService.getBus(), clock);
CallContext context = new DefaultCallContextFactory(clock).createCallContext("Migration test", CallOrigin.TEST, UserType.TEST);
Invoice invoice = dispatcher.processAccount(accountId, date_regular, true, context);
@@ -209,7 +213,7 @@ public class TestDefaultInvoiceMigrationApi {
}
// Check migration invoice is NOT returned for all user api invoice calls
- @Test(groups={"slow"},enabled=true)
+ @Test(groups={"slow"}, enabled=true)
public void testUserApiAccess(){
List<Invoice> byAccount = invoiceUserApi.getInvoicesByAccount(accountId);
Assert.assertEquals(byAccount.size(),1);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
index a6980bc..08354db 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
@@ -70,12 +70,10 @@ public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
protected void setup() throws IOException {
module = new InvoiceModuleWithEmbeddedDb();
final String invoiceDdl = IOUtils.toString(DefaultInvoiceDao.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
- final String entitlementDdl = IOUtils.toString(DefaultInvoiceDao.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
final String utilDdl = IOUtils.toString(DefaultInvoiceDao.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
module.startDb();
module.initDb(invoiceDdl);
- module.initDb(entitlementDdl);
module.initDb(utilDdl);
final Injector injector = Guice.createInjector(Stage.DEVELOPMENT, module);
@@ -103,21 +101,9 @@ public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
@Override
public Void inTransaction(Handle h, TransactionStatus status)
throws Exception {
- //h.execute("truncate table accounts");
- //h.execute("truncate table entitlement_events");
- //h.execute("truncate table subscriptions");
- //h.execute("truncate table bundles");
- //h.execute("truncate table notifications");
- //h.execute("truncate table claimed_notifications");
h.execute("truncate table invoices");
h.execute("truncate table fixed_invoice_items");
h.execute("truncate table recurring_invoice_items");
- //h.execute("truncate table tag_definitions");
- //h.execute("truncate table tags");
- //h.execute("truncate table custom_fields");
- //h.execute("truncate table invoice_payments");
- //h.execute("truncate table payment_attempts");
- //h.execute("truncate table payments");
return null;
}
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 85d1c16..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
@@ -53,12 +53,11 @@ import com.ning.billing.invoice.model.BillingEventSet;
import com.ning.billing.invoice.model.DefaultInvoice;
import com.ning.billing.invoice.model.DefaultInvoicePayment;
import com.ning.billing.invoice.model.RecurringInvoiceItem;
-import com.ning.billing.junction.plumbing.billing.DefaultBillingEvent;
import com.ning.billing.mock.BrainDeadProxyFactory;
import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
import com.ning.billing.util.tag.ControlTagType;
-@Test(groups = {"invoicing", "invoicing-invoiceDao"})
+@Test(groups = {"slow", "invoicing", "invoicing-invoiceDao"})
public class InvoiceDaoTests extends InvoiceDaoTestBase {
@Test
public void testCreationAndRetrievalByAccount() {
@@ -407,11 +406,10 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
MockPlanPhase phase1 = new MockPlanPhase(recurringPrice, null, BillingPeriod.MONTHLY, PhaseType.TRIAL);
MockPlan plan1 = new MockPlan(phase1);
- Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
- ((ZombieControl) subscription).addResult("getId", UUID.randomUUID());
+ Subscription subscription = getZombieSubscription();
DateTime effectiveDate1 = new DateTime(2011, 2, 1, 0, 0, 0, 0);
- BillingEvent event1 = new DefaultBillingEvent(null, subscription, effectiveDate1, plan1, phase1, null,
+ BillingEvent event1 = createMockBillingEvent(null, subscription, effectiveDate1, plan1, phase1, null,
recurringPrice.getPrice(currency), currency, BillingPeriod.MONTHLY, 1, BillingModeType.IN_ADVANCE,
"testEvent1", 1L, SubscriptionTransitionType.CREATE);
@@ -429,7 +427,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
MockPlan plan2 = new MockPlan(phase2);
DateTime effectiveDate2 = new DateTime(2011, 2, 15, 0, 0, 0, 0);
- BillingEvent event2 = new DefaultBillingEvent(null, subscription, effectiveDate2, plan2, phase2, null,
+ BillingEvent event2 = createMockBillingEvent(null, subscription, effectiveDate2, plan2, phase2, null,
recurringPrice2.getPrice(currency), currency, BillingPeriod.MONTHLY, 1, BillingModeType.IN_ADVANCE,
"testEvent2", 2L, SubscriptionTransitionType.CREATE);
events.add(event2);
@@ -458,11 +456,10 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
MockPlanPhase phase = new MockPlanPhase(recurringPrice, null);
MockPlan plan = new MockPlan(phase);
- Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
- ((ZombieControl) subscription).addResult("getId", UUID.randomUUID());
+ Subscription subscription = getZombieSubscription();
DateTime effectiveDate = buildDateTime(2011, 1, 1);
- BillingEvent event = new DefaultBillingEvent(null, subscription, effectiveDate, plan, phase, null,
+ BillingEvent event = createMockBillingEvent(null, subscription, effectiveDate, plan, phase, null,
recurringPrice.getPrice(currency), currency, BillingPeriod.MONTHLY, 15, BillingModeType.IN_ADVANCE,
"testEvent", 1L, SubscriptionTransitionType.CREATE);
BillingEventSet events = new BillingEventSet();
@@ -476,6 +473,13 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
assertEquals(invoice.getTotalAmount().compareTo(ZERO), 0);
}
+ private Subscription getZombieSubscription() {
+ Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
+ ((ZombieControl) subscription).addResult("getId", UUID.randomUUID());
+ ((ZombieControl) subscription).addResult("getBundleId", UUID.randomUUID());
+ return subscription;
+ }
+
@Test
public void testInvoiceForFreeTrialWithRecurringDiscount() throws InvoiceApiException, CatalogApiException {
Currency currency = Currency.USD;
@@ -491,11 +495,10 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
MockPlan plan = new MockPlan();
- Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
- ((ZombieControl) subscription).addResult("getId", UUID.randomUUID());
+ Subscription subscription = getZombieSubscription();
DateTime effectiveDate1 = buildDateTime(2011, 1, 1);
- BillingEvent event1 = new DefaultBillingEvent(null, subscription, effectiveDate1, plan, phase1, fixedPrice.getPrice(currency),
+ BillingEvent event1 = createMockBillingEvent(null, subscription, effectiveDate1, plan, phase1, fixedPrice.getPrice(currency),
null, currency, BillingPeriod.MONTHLY, 1, BillingModeType.IN_ADVANCE,
"testEvent1", 1L, SubscriptionTransitionType.CREATE);
BillingEventSet events = new BillingEventSet();
@@ -511,7 +514,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
invoiceList.add(invoice1);
DateTime effectiveDate2 = effectiveDate1.plusDays(30);
- BillingEvent event2 = new DefaultBillingEvent(null, subscription, effectiveDate2, plan, phase2, null,
+ BillingEvent event2 = createMockBillingEvent(null, subscription, effectiveDate2, plan, phase2, null,
recurringPrice.getPrice(currency), currency, BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
"testEvent2", 2L, SubscriptionTransitionType.CHANGE);
events.add(event2);
@@ -551,11 +554,10 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
MockPlan plan = new MockPlan();
- Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
- ((ZombieControl) subscription).addResult("getId", UUID.randomUUID());
+ Subscription subscription = getZombieSubscription();
DateTime effectiveDate1 = buildDateTime(2011, 1, 1);
- BillingEvent event1 = new DefaultBillingEvent(null, subscription, effectiveDate1, plan, phase1,
+ BillingEvent event1 = createMockBillingEvent(null, subscription, effectiveDate1, plan, phase1,
fixedPrice.getPrice(currency), null, currency,
BillingPeriod.MONTHLY, 1, BillingModeType.IN_ADVANCE,
"testEvent1", 1L, SubscriptionTransitionType.CREATE);
@@ -563,7 +565,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
events.add(event1);
DateTime effectiveDate2 = effectiveDate1.plusDays(30);
- BillingEvent event2 = new DefaultBillingEvent(null, subscription, effectiveDate2, plan, phase2, null,
+ BillingEvent event2 = createMockBillingEvent(null, subscription, effectiveDate2, plan, phase2, null,
recurringPrice.getPrice(currency), currency, BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
"testEvent2", 2L, SubscriptionTransitionType.CHANGE);
events.add(event2);
@@ -587,8 +589,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
DateTime targetDate1 = DateTime.now().plusMonths(1);
DateTime targetDate2 = DateTime.now().plusMonths(2);
- Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
- ((ZombieControl) subscription).addResult("getId", UUID.randomUUID());
+ Subscription subscription = getZombieSubscription();
Plan plan = BrainDeadProxyFactory.createBrainDeadProxyFor(Plan.class);
((ZombieControl) plan).addResult("getName", "plan");
@@ -602,10 +603,10 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
BillingEventSet events = new BillingEventSet();
List<Invoice> invoices = new ArrayList<Invoice>();
- BillingEvent event1 = new DefaultBillingEvent(null, subscription, targetDate1, plan, phase1, null,
- TEN, currency,
- BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
- "testEvent1", 1L, SubscriptionTransitionType.CHANGE);
+ BillingEvent event1 = createMockBillingEvent(null, subscription, targetDate1, plan, phase1, null,
+ TEN, currency,
+ BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
+ "testEvent1", 1L, SubscriptionTransitionType.CHANGE);
events.add(event1);
Invoice invoice1 = generator.generateInvoice(UUID.randomUUID(), events, invoices, targetDate1, Currency.USD);
@@ -614,10 +615,10 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
invoice1 = invoiceDao.getById(invoice1.getId());
assertNotNull(invoice1.getInvoiceNumber());
- BillingEvent event2 = new DefaultBillingEvent(null, subscription, targetDate1, plan, phase2, null,
- TWENTY, currency,
- BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
- "testEvent2", 2L, SubscriptionTransitionType.CHANGE);
+ BillingEvent event2 = createMockBillingEvent(null, subscription, targetDate1, plan, phase2, null,
+ TWENTY, currency,
+ BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
+ "testEvent2", 2L, SubscriptionTransitionType.CHANGE);
events.add(event2);
Invoice invoice2 = generator.generateInvoice(UUID.randomUUID(), events, invoices, targetDate2, Currency.USD);
invoiceDao.create(invoice2, context);
@@ -627,8 +628,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
@Test
public void testAddingWrittenOffTag() throws InvoiceApiException {
- Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
- ((ZombieControl) subscription).addResult("getId", UUID.randomUUID());
+ Subscription subscription = getZombieSubscription();
Plan plan = BrainDeadProxyFactory.createBrainDeadProxyFor(Plan.class);
((ZombieControl) plan).addResult("getName", "plan");
@@ -640,10 +640,10 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
Currency currency = Currency.USD;
// create pseudo-random invoice
- BillingEvent event1 = new DefaultBillingEvent(null, subscription, targetDate1, plan, phase1, null,
- TEN, currency,
- BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
- "testEvent1", 1L, SubscriptionTransitionType.CHANGE);
+ BillingEvent event1 = createMockBillingEvent(null, subscription, targetDate1, plan, phase1, null,
+ TEN, currency,
+ BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
+ "testEvent1", 1L, SubscriptionTransitionType.CHANGE);
BillingEventSet events = new BillingEventSet();
events.add(event1);
@@ -653,13 +653,12 @@ 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
public void testRemoveWrittenOffTag() throws InvoiceApiException {
- Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
- ((ZombieControl) subscription).addResult("getId", UUID.randomUUID());
+ Subscription subscription = getZombieSubscription();
Plan plan = BrainDeadProxyFactory.createBrainDeadProxyFor(Plan.class);
((ZombieControl) plan).addResult("getName", "plan");
@@ -671,10 +670,10 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
Currency currency = Currency.USD;
// create pseudo-random invoice
- BillingEvent event1 = new DefaultBillingEvent(null, subscription, targetDate1, plan, phase1, null,
- TEN, currency,
- BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
- "testEvent1", 1L, SubscriptionTransitionType.CHANGE);
+ BillingEvent event1 = createMockBillingEvent(null, subscription, targetDate1, plan, phase1, null,
+ TEN, currency,
+ BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
+ "testEvent1", 1L, SubscriptionTransitionType.CHANGE);
BillingEventSet events = new BillingEventSet();
events.add(event1);
@@ -684,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/glue/InvoiceModuleWithEmbeddedDb.java b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithEmbeddedDb.java
index 2d46604..5d6f36f 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithEmbeddedDb.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithEmbeddedDb.java
@@ -27,6 +27,7 @@ import com.ning.billing.account.api.AccountUserApi;
import com.ning.billing.catalog.glue.CatalogModule;
import com.ning.billing.dbi.MysqlTestingHelper;
import com.ning.billing.entitlement.glue.DefaultEntitlementModule;
+import com.ning.billing.invoice.api.InvoiceNotifier;
import com.ning.billing.invoice.api.test.DefaultInvoiceTestApi;
import com.ning.billing.invoice.api.test.InvoiceTestApi;
import com.ning.billing.invoice.dao.InvoicePaymentSqlDao;
@@ -35,8 +36,10 @@ import com.ning.billing.invoice.notification.MockNextBillingDateNotifier;
import com.ning.billing.invoice.notification.MockNextBillingDatePoster;
import com.ning.billing.invoice.notification.NextBillingDateNotifier;
import com.ning.billing.invoice.notification.NextBillingDatePoster;
+import com.ning.billing.invoice.notification.NullInvoiceNotifier;
import com.ning.billing.junction.api.BillingApi;
import com.ning.billing.mock.BrainDeadProxyFactory;
+import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
import com.ning.billing.util.callcontext.CallContextFactory;
import com.ning.billing.util.callcontext.DefaultCallContextFactory;
import com.ning.billing.util.clock.Clock;
@@ -81,9 +84,10 @@ public class InvoiceModuleWithEmbeddedDb extends DefaultInvoiceModule {
}
@Override
- protected void installNotifier() {
+ protected void installNotifiers() {
bind(NextBillingDateNotifier.class).to(MockNextBillingDateNotifier.class).asEagerSingleton();
bind(NextBillingDatePoster.class).to(MockNextBillingDatePoster.class).asEagerSingleton();
+ bind(InvoiceNotifier.class).to(NullInvoiceNotifier.class).asEagerSingleton();
}
@Override
@@ -95,13 +99,17 @@ public class InvoiceModuleWithEmbeddedDb extends DefaultInvoiceModule {
bind(Clock.class).to(DefaultClock.class).asEagerSingleton();
bind(CallContextFactory.class).to(DefaultCallContextFactory.class).asEagerSingleton();
- install(new FieldStoreModule());
+ install(new FieldStoreModule());
install(new TagStoreModule());
installNotificationQueue();
// install(new AccountModule());
bind(AccountUserApi.class).toInstance(BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class));
- bind(BillingApi.class).toInstance(BrainDeadProxyFactory.createBrainDeadProxyFor(BillingApi.class));
+
+ BillingApi billingApi = BrainDeadProxyFactory.createBrainDeadProxyFor(BillingApi.class);
+ ((ZombieControl) billingApi).addResult("setChargedThroughDateFromTransaction", BrainDeadProxyFactory.ZOMBIE_VOID);
+ bind(BillingApi.class).toInstance(billingApi);
+
install(new CatalogModule());
install(new DefaultEntitlementModule());
install(new GlobalLockerModule());
diff --git a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java
index 741fa5a..bd43d0a 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java
@@ -16,33 +16,31 @@
package com.ning.billing.invoice.glue;
+import com.ning.billing.invoice.api.InvoiceNotifier;
import com.ning.billing.invoice.dao.InvoiceDao;
import com.ning.billing.invoice.dao.MockInvoiceDao;
+import com.ning.billing.invoice.notification.NullInvoiceNotifier;
import com.ning.billing.util.globallocker.GlobalLocker;
import com.ning.billing.util.globallocker.MockGlobalLocker;
import com.ning.billing.util.glue.FieldStoreModule;
public class InvoiceModuleWithMocks extends DefaultInvoiceModule {
- @Override
+ @Override
protected void installInvoiceDao() {
bind(MockInvoiceDao.class).asEagerSingleton();
bind(InvoiceDao.class).to(MockInvoiceDao.class);
bind(GlobalLocker.class).to(MockGlobalLocker.class).asEagerSingleton();
}
- protected void installGlobalLocker() {
- bind(GlobalLocker.class).to(MockGlobalLocker.class).asEagerSingleton();
- }
-
@Override
protected void installInvoiceListener() {
}
@Override
- protected void installNotifier() {
-
+ protected void installNotifiers() {
+ bind(InvoiceNotifier.class).to(NullInvoiceNotifier.class).asEagerSingleton();
}
@Override
@@ -60,6 +58,5 @@ public class InvoiceModuleWithMocks extends DefaultInvoiceModule {
super.configure();
install(new FieldStoreModule());
- //install(new TagStoreModule());
}
}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/HtmlInvoiceGeneratorTest.java b/invoice/src/test/java/com/ning/billing/invoice/HtmlInvoiceGeneratorTest.java
new file mode 100644
index 0000000..95f39c9
--- /dev/null
+++ b/invoice/src/test/java/com/ning/billing/invoice/HtmlInvoiceGeneratorTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.formatters.InvoiceFormatterFactory;
+import com.ning.billing.invoice.template.HtmlInvoiceGenerator;
+import com.ning.billing.invoice.template.formatters.DefaultInvoiceFormatterFactory;
+import com.ning.billing.mock.BrainDeadProxyFactory;
+import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
+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.joda.time.DateTime;
+import org.skife.config.ConfigurationObjectFactory;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import static org.testng.Assert.assertNotNull;
+
+@Test(groups = {"fast", "email"})
+public class HtmlInvoiceGeneratorTest {
+ private HtmlInvoiceGenerator g;
+ private final static String TEST_TEMPLATE_NAME = "HtmlInvoiceTemplate";
+
+ @BeforeClass
+ public void setup() {
+ TranslatorConfig config = new ConfigurationObjectFactory(System.getProperties()).build(TranslatorConfig.class);
+ TemplateEngine templateEngine = new MustacheTemplateEngine();
+ InvoiceFormatterFactory factory = new DefaultInvoiceFormatterFactory();
+ g = new HtmlInvoiceGenerator(factory, templateEngine, config);
+ }
+
+ @Test
+ public void testGenerateInvoice() throws Exception {
+ String output = g.generateInvoice(createAccount(), createInvoice(), TEST_TEMPLATE_NAME);
+ assertNotNull(output);
+ System.out.print(output);
+ }
+
+ private Account createAccount() {
+ Account account = BrainDeadProxyFactory.createBrainDeadProxyFor(Account.class);
+ ZombieControl zombieControl = (ZombieControl) account;
+ zombieControl.addResult("getExternalKey", "1234abcd");
+ zombieControl.addResult("getName", "Jim Smith");
+ zombieControl.addResult("getFirstNameLength", 3);
+ zombieControl.addResult("getEmail", "jim.smith@mail.com");
+ zombieControl.addResult("getLocale", Locale.US.toString());
+ zombieControl.addResult("getAddress1", "123 Some Street");
+ zombieControl.addResult("getAddress2", "Apt 456");
+ zombieControl.addResult("getCity", "Some City");
+ zombieControl.addResult("getStateOrProvince", "Some State");
+ zombieControl.addResult("getPostalCode", "12345-6789");
+ zombieControl.addResult("getCountry", "USA");
+ zombieControl.addResult("getPhone", "123-456-7890");
+
+ return account;
+ }
+
+ private Invoice createInvoice() {
+ DateTime startDate = new DateTime().minusMonths(1);
+ DateTime endDate = new DateTime();
+
+ BigDecimal price1 = new BigDecimal("29.95");
+ BigDecimal price2 = new BigDecimal("59.95");
+ Invoice dummyInvoice = BrainDeadProxyFactory.createBrainDeadProxyFor(Invoice.class);
+ ZombieControl zombie = (ZombieControl) dummyInvoice;
+ zombie.addResult("getInvoiceDate", startDate);
+ zombie.addResult("getInvoiceNumber", 42);
+ zombie.addResult("getCurrency", Currency.USD);
+ zombie.addResult("getTotalAmount", price1.add(price2));
+ zombie.addResult("getAmountPaid", BigDecimal.ZERO);
+ zombie.addResult("getBalance", price1.add(price2));
+
+ List<InvoiceItem> items = new ArrayList<InvoiceItem>();
+ items.add(createInvoiceItem(price1, "Domain 1", startDate, endDate, "ning-plus"));
+ items.add(createInvoiceItem(price2, "Domain 2", startDate, endDate, "ning-pro"));
+ zombie.addResult("getInvoiceItems", items);
+
+ return dummyInvoice;
+ }
+
+ private InvoiceItem createInvoiceItem(BigDecimal amount, String networkName, DateTime startDate, DateTime endDate, String planName) {
+ InvoiceItem item = BrainDeadProxyFactory.createBrainDeadProxyFor(InvoiceItem.class);
+ ZombieControl zombie = (ZombieControl) item;
+ zombie.addResult("getAmount", amount);
+ zombie.addResult("getStartDate", startDate);
+ zombie.addResult("getEndDate", endDate);
+ zombie.addResult("getPlanName", planName);
+ zombie.addResult("getDescription", networkName);
+
+
+ return item;
+ }
+}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/MockModule.java b/invoice/src/test/java/com/ning/billing/invoice/MockModule.java
index e57ea87..aa4abf6 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/MockModule.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/MockModule.java
@@ -20,29 +20,26 @@ import org.skife.config.ConfigurationObjectFactory;
import org.skife.jdbi.v2.IDBI;
import com.google.inject.AbstractModule;
-import com.ning.billing.account.api.AccountUserApi;
import com.ning.billing.catalog.glue.CatalogModule;
import com.ning.billing.dbi.DBIProvider;
import com.ning.billing.dbi.DbiConfig;
import com.ning.billing.dbi.MysqlTestingHelper;
-import com.ning.billing.entitlement.glue.DefaultEntitlementModule;
+import com.ning.billing.invoice.api.formatters.InvoiceFormatterFactory;
import com.ning.billing.invoice.glue.DefaultInvoiceModule;
-import com.ning.billing.mock.BrainDeadProxyFactory;
+import com.ning.billing.invoice.template.formatters.DefaultInvoiceFormatterFactory;
import com.ning.billing.mock.glue.MockJunctionModule;
import com.ning.billing.util.callcontext.CallContextFactory;
import com.ning.billing.util.callcontext.DefaultCallContextFactory;
import com.ning.billing.util.clock.Clock;
import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.email.EmailModule;
import com.ning.billing.util.glue.BusModule;
import com.ning.billing.util.glue.FieldStoreModule;
import com.ning.billing.util.glue.GlobalLockerModule;
import com.ning.billing.util.glue.NotificationQueueModule;
import com.ning.billing.util.glue.TagStoreModule;
-
public class MockModule extends AbstractModule {
- public static final String PLUGIN_NAME = "yoyo";
-
@Override
protected void configure() {
bind(Clock.class).to(ClockMock.class).asEagerSingleton();
@@ -62,21 +59,18 @@ public class MockModule extends AbstractModule {
bind(IDBI.class).toInstance(dbi);
}
+ bind(InvoiceFormatterFactory.class).to(DefaultInvoiceFormatterFactory.class).asEagerSingleton();
+
+ install(new EmailModule());
install(new GlobalLockerModule());
install(new NotificationQueueModule());
- installEntitlementModule();
install(new CatalogModule());
install(new BusModule());
installInvoiceModule();
install(new MockJunctionModule());
}
-
- protected void installEntitlementModule() {
- install(new DefaultEntitlementModule());
- }
protected void installInvoiceModule() {
install(new DefaultInvoiceModule());
}
-
}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java b/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
index 6419bcf..4d6b6fe 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
@@ -63,11 +63,12 @@ import com.ning.billing.entitlement.engine.dao.RepairEntitlementDao;
import com.ning.billing.entitlement.glue.DefaultEntitlementModule;
import com.ning.billing.invoice.InvoiceDispatcher;
import com.ning.billing.invoice.InvoiceListener;
+import com.ning.billing.invoice.api.InvoiceNotifier;
import com.ning.billing.invoice.dao.DefaultInvoiceDao;
import com.ning.billing.invoice.dao.InvoiceDao;
import com.ning.billing.invoice.model.DefaultInvoiceGenerator;
import com.ning.billing.invoice.model.InvoiceGenerator;
-import com.ning.billing.lifecycle.KillbillService.ServiceException;
+import com.ning.billing.lifecycle.KillbillService;
import com.ning.billing.mock.BrainDeadProxyFactory;
import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
import com.ning.billing.mock.glue.MockJunctionModule;
@@ -88,7 +89,6 @@ import com.ning.billing.util.notificationq.dao.NotificationSqlDao;
import com.ning.billing.util.tag.dao.AuditedTagDao;
import com.ning.billing.util.tag.dao.TagDao;
-
public class TestNextBillingDateNotifier {
private Clock clock;
private DefaultNextBillingDateNotifier notifier;
@@ -130,7 +130,7 @@ public class TestNextBillingDateNotifier {
}
@BeforeClass(groups={"slow"})
- public void setup() throws ServiceException, IOException, ClassNotFoundException, SQLException {
+ public void setup() throws KillbillService.ServiceException, IOException, ClassNotFoundException, SQLException {
//TestApiBase.loadSystemPropertiesFromClasspath("/entitlement.properties");
final Injector g = Guice.createInjector(Stage.PRODUCTION, new AbstractModule() {
@@ -139,6 +139,7 @@ public class TestNextBillingDateNotifier {
bind(CallContextFactory.class).to(DefaultCallContextFactory.class).asEagerSingleton();
bind(Bus.class).to(InMemoryBus.class).asEagerSingleton();
bind(NotificationQueueService.class).to(DefaultNotificationQueueService.class).asEagerSingleton();
+ bind(InvoiceNotifier.class).to(NullInvoiceNotifier.class).asEagerSingleton();
final InvoiceConfig invoiceConfig = new ConfigurationObjectFactory(System.getProperties()).build(InvoiceConfig.class);
bind(InvoiceConfig.class).toInstance(invoiceConfig);
final CatalogConfig catalogConfig = new ConfigurationObjectFactory(System.getProperties()).build(CatalogConfig.class);
@@ -196,11 +197,9 @@ public class TestNextBillingDateNotifier {
private void startMysql() throws IOException, ClassNotFoundException, SQLException {
final String ddl = IOUtils.toString(NotificationSqlDao.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
final String testDdl = IOUtils.toString(NotificationSqlDao.class.getResourceAsStream("/com/ning/billing/util/ddl_test.sql"));
- final String entitlementDdl = IOUtils.toString(NotificationSqlDao.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
helper.startMysql();
helper.initDb(ddl);
helper.initDb(testDdl);
- helper.initDb(entitlementDdl);
}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
index 68a024e..e591dfa 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
@@ -23,6 +23,9 @@ import java.util.SortedSet;
import java.util.TreeSet;
import java.util.UUID;
+import com.ning.billing.invoice.api.InvoiceNotifier;
+import com.ning.billing.invoice.notification.NullInvoiceNotifier;
+import com.ning.billing.invoice.tests.InvoicingTestBase;
import org.apache.commons.io.IOUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
@@ -53,7 +56,6 @@ import com.ning.billing.invoice.dao.InvoiceDao;
import com.ning.billing.invoice.model.InvoiceGenerator;
import com.ning.billing.invoice.notification.NextBillingDateNotifier;
import com.ning.billing.junction.api.BillingApi;
-import com.ning.billing.junction.plumbing.billing.DefaultBillingEvent;
import com.ning.billing.mock.BrainDeadProxyFactory;
import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
import com.ning.billing.mock.glue.MockJunctionModule;
@@ -68,7 +70,7 @@ import com.ning.billing.util.globallocker.GlobalLocker;
@Test(groups = "slow")
@Guice(modules = {MockModule.class})
-public class TestInvoiceDispatcher {
+public class TestInvoiceDispatcher extends InvoicingTestBase {
private Logger log = LoggerFactory.getLogger(TestInvoiceDispatcher.class);
@Inject
@@ -100,13 +102,11 @@ public class TestInvoiceDispatcher {
@BeforeSuite(groups = "slow")
public void setup() throws IOException
{
- final String entitlementDdl = IOUtils.toString(TestInvoiceDispatcher.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
final String invoiceDdl = IOUtils.toString(TestInvoiceDispatcher.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
final String utilDdl = IOUtils.toString(TestInvoiceDispatcher.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
helper.startMysql();
- helper.initDb(entitlementDdl);
helper.initDb(invoiceDdl);
helper.initDb(utilDdl);
notifier.initialize();
@@ -140,6 +140,7 @@ public class TestInvoiceDispatcher {
((ZombieControl)accountUserApi).addResult("getAccountById", account);
((ZombieControl)account).addResult("getCurrency", Currency.USD);
((ZombieControl)account).addResult("getId", accountId);
+ ((ZombieControl)account).addResult(("isNotifiedForInvoices"), true);
Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
((ZombieControl)subscription).addResult("getId", subscriptionId);
@@ -150,16 +151,18 @@ public class TestInvoiceDispatcher {
DateTime effectiveDate = new DateTime().minusDays(1);
Currency currency = Currency.USD;
BigDecimal fixedPrice = null;
- events.add(new DefaultBillingEvent(account, subscription, effectiveDate,plan, planPhase,
- fixedPrice, BigDecimal.ONE, currency, BillingPeriod.MONTHLY, 1,
- BillingModeType.IN_ADVANCE, "", 1L, SubscriptionTransitionType.CREATE));
+ events.add(createMockBillingEvent(account, subscription, effectiveDate, plan, planPhase,
+ fixedPrice, BigDecimal.ONE, currency, BillingPeriod.MONTHLY, 1,
+ BillingModeType.IN_ADVANCE, "", 1L, SubscriptionTransitionType.CREATE));
BillingApi entitlementBillingApi = BrainDeadProxyFactory.createBrainDeadProxyFor(BillingApi.class);
- ((ZombieControl)entitlementBillingApi).addResult("getBillingEventsForAccountAndUpdateAccountBCD", events);
+ ((ZombieControl) entitlementBillingApi).addResult("getBillingEventsForAccountAndUpdateAccountBCD", events);
DateTime target = new DateTime();
- InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountUserApi, entitlementBillingApi, invoiceDao, locker, busService.getBus(), clock);
+ InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
+ InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountUserApi, entitlementBillingApi, invoiceDao,
+ invoiceNotifier, locker, busService.getBus(), clock);
Invoice invoice = dispatcher.processAccount(accountId, target, true, context);
Assert.assertNotNull(invoice);
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 1eaa8af..49fca25 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;
@@ -44,9 +47,7 @@ import com.ning.billing.config.InvoiceConfig;
import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.billing.BillingEvent;
import com.ning.billing.entitlement.api.billing.BillingModeType;
-import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.api.user.SubscriptionData;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.invoice.model.BillingEventSet;
@@ -54,7 +55,6 @@ import com.ning.billing.invoice.model.DefaultInvoiceGenerator;
import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
import com.ning.billing.invoice.model.InvoiceGenerator;
import com.ning.billing.invoice.model.RecurringInvoiceItem;
-import com.ning.billing.junction.plumbing.billing.DefaultBillingEvent;
import com.ning.billing.mock.BrainDeadProxyFactory;
import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
import com.ning.billing.util.clock.Clock;
@@ -62,38 +62,38 @@ import com.ning.billing.util.clock.DefaultClock;
@Test(groups = {"fast", "invoicing", "invoiceGenerator"})
public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
- private final Clock clock = new DefaultClock();
- private final InvoiceConfig invoiceConfig = new InvoiceConfig() {
- @Override
- public long getDaoClaimTimeMs() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int getDaoMaxReadyEvents() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public long getNotificationSleepTimeMs() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int getNumberOfMonthsInFuture() {
- return 36;
- }
-
- @Override
- public boolean isNotificationProcessingOff() {
- throw new UnsupportedOperationException();
- }
- };
-
private final InvoiceGenerator generator;
public DefaultInvoiceGeneratorTests() {
super();
+
+ Clock clock = new DefaultClock();
+ InvoiceConfig invoiceConfig = new InvoiceConfig() {
+ @Override
+ public long getDaoClaimTimeMs() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getDaoMaxReadyEvents() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long getNotificationSleepTimeMs() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getNumberOfMonthsInFuture() {
+ return 36;
+ }
+
+ @Override
+ public boolean isNotificationProcessingOff() {
+ throw new UnsupportedOperationException();
+ }
+ };
this.generator = new DefaultInvoiceGenerator(clock, invoiceConfig);
}
@@ -119,7 +119,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
public void testWithSingleMonthlyEvent() throws InvoiceApiException, CatalogApiException {
BillingEventSet events = new BillingEventSet();
- Subscription sub = new SubscriptionData(new SubscriptionBuilder().setId(UUID.randomUUID()));
+ Subscription sub = createZombieSubscription();
DateTime startDate = buildDateTime(2011, 9, 1);
Plan plan = new MockPlan();
@@ -139,11 +139,23 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
assertEquals(invoice.getInvoiceItems().get(0).getSubscriptionId(), sub.getId());
}
+ private Subscription createZombieSubscription() {
+ return createZombieSubscription(UUID.randomUUID());
+ }
+
+ private Subscription createZombieSubscription(UUID subscriptionId) {
+ Subscription sub = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
+ ((ZombieControl) sub).addResult("getId", subscriptionId);
+ ((ZombieControl) sub).addResult("getBundleId", UUID.randomUUID());
+
+ return sub;
+ }
+
@Test
public void testWithSingleMonthlyEventWithLeadingProRation() throws InvoiceApiException, CatalogApiException {
BillingEventSet events = new BillingEventSet();
- Subscription sub = new SubscriptionData(new SubscriptionBuilder().setId(UUID.randomUUID()));
+ Subscription sub = createZombieSubscription();
DateTime startDate = buildDateTime(2011, 9, 1);
Plan plan = new MockPlan();
@@ -177,7 +189,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
BigDecimal rate2 = TEN;
PlanPhase phase2 = createMockMonthlyPlanPhase(rate2);
- Subscription sub = new SubscriptionData(new SubscriptionBuilder().setId(UUID.randomUUID()));
+ Subscription sub = createZombieSubscription();
BillingEvent event1 = createBillingEvent(sub.getId(), buildDateTime(2011, 9, 1), plan1, phase1, 1);
events.add(event1);
@@ -202,7 +214,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
BigDecimal rate1 = FIVE;
PlanPhase phase1 = createMockMonthlyPlanPhase(rate1);
- Subscription sub = new SubscriptionData(new SubscriptionBuilder().setId(UUID.randomUUID()));
+ Subscription sub = createZombieSubscription();
BillingEvent event1 = createBillingEvent(sub.getId(), buildDateTime(2011, 9, 1), plan1,phase1, 1);
events.add(event1);
@@ -239,7 +251,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
BigDecimal rate1 = FIVE;
PlanPhase phase1 = createMockMonthlyPlanPhase(rate1);
- Subscription sub = new SubscriptionData(new SubscriptionBuilder().setId(UUID.randomUUID()));
+ Subscription sub = createZombieSubscription();
BillingEvent event1 = createBillingEvent(sub.getId(), buildDateTime(2011, 9, 1), plan1, phase1, 1);
events.add(event1);
@@ -266,7 +278,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
public void testSingleEventWithExistingInvoice() throws InvoiceApiException, CatalogApiException {
BillingEventSet events = new BillingEventSet();
- Subscription sub = new SubscriptionData(new SubscriptionBuilder().setId(UUID.randomUUID()));
+ Subscription sub = createZombieSubscription();
DateTime startDate = buildDateTime(2011, 9, 1);
Plan plan1 = new MockPlan();
@@ -480,9 +492,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
@Test
public void testFixedPriceLifeCycle() throws InvoiceApiException {
UUID accountId = UUID.randomUUID();
- Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
- ((ZombieControl) subscription).addResult("getId", UUID.randomUUID());
- ((ZombieControl) subscription).addResult("getBundleId", UUID.randomUUID());
+ Subscription subscription = createZombieSubscription();
Plan plan = new MockPlan("plan 1");
MockInternationalPrice zeroPrice = new MockInternationalPrice(new DefaultPrice(ZERO, Currency.USD));
@@ -495,17 +505,17 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
BillingEventSet events = new BillingEventSet();
- BillingEvent event1 = new DefaultBillingEvent(null, subscription, new DateTime("2012-01-1T00:00:00.000-08:00"),
- plan, phase1,
- ZERO, null, Currency.USD, BillingPeriod.NO_BILLING_PERIOD, 1,
- BillingModeType.IN_ADVANCE, "Test Event 1", 1L,
- SubscriptionTransitionType.CREATE);
+ BillingEvent event1 = createMockBillingEvent(null, subscription, new DateTime("2012-01-1T00:00:00.000-08:00"),
+ plan, phase1,
+ ZERO, null, Currency.USD, BillingPeriod.NO_BILLING_PERIOD, 1,
+ BillingModeType.IN_ADVANCE, "Test Event 1", 1L,
+ SubscriptionTransitionType.CREATE);
- BillingEvent event2 = new DefaultBillingEvent(null, subscription, changeDate,
- plan, phase2,
- ZERO, null, Currency.USD, BillingPeriod.NO_BILLING_PERIOD, 1,
- BillingModeType.IN_ADVANCE, "Test Event 2", 2L,
- SubscriptionTransitionType.PHASE);
+ BillingEvent event2 = createMockBillingEvent(null, subscription, changeDate,
+ plan, phase2,
+ ZERO, null, Currency.USD, BillingPeriod.NO_BILLING_PERIOD, 1,
+ BillingModeType.IN_ADVANCE, "Test Event 2", 2L,
+ SubscriptionTransitionType.PHASE);
events.add(event2);
events.add(event1);
@@ -688,16 +698,16 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
null, BillingPeriod.ANNUAL, phaseType);
}
- private DefaultBillingEvent createBillingEvent(final UUID subscriptionId, final DateTime startDate,
+ private BillingEvent createBillingEvent(final UUID subscriptionId, final DateTime startDate,
final Plan plan, final PlanPhase planPhase, final int billCycleDay) throws CatalogApiException {
- Subscription sub = new SubscriptionData(new SubscriptionBuilder().setId(subscriptionId));
+ Subscription sub = createZombieSubscription(subscriptionId);
Currency currency = Currency.USD;
- return new DefaultBillingEvent(null, sub, startDate, plan, planPhase,
- planPhase.getFixedPrice() == null ? null : planPhase.getFixedPrice().getPrice(currency),
- planPhase.getRecurringPrice() == null ? null : planPhase.getRecurringPrice().getPrice(currency),
- currency, planPhase.getBillingPeriod(),
- billCycleDay, BillingModeType.IN_ADVANCE, "Test", 1L, SubscriptionTransitionType.CREATE);
+ return createMockBillingEvent(null, sub, startDate, plan, planPhase,
+ planPhase.getFixedPrice() == null ? null : planPhase.getFixedPrice().getPrice(currency),
+ planPhase.getRecurringPrice() == null ? null : planPhase.getRecurringPrice().getPrice(currency),
+ currency, planPhase.getBillingPeriod(),
+ billCycleDay, BillingModeType.IN_ADVANCE, "Test", 1L, SubscriptionTransitionType.CREATE);
}
private void testInvoiceGeneration(final UUID accountId, final BillingEventSet events, final List<Invoice> existingInvoices,
@@ -712,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(enabled = false)
+ 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/inAdvance/monthly/ProRationTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/ProRationTests.java
index 8d564c8..78f06b4 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/ProRationTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/ProRationTests.java
@@ -215,9 +215,10 @@ public class ProRationTests extends ProRationInAdvanceTestBase {
DateTime targetDate = buildDateTime(2011, 4, 21);
BigDecimal expectedValue;
- expectedValue = SEVEN.divide(THIRTY_ONE, NUMBER_OF_DECIMALS, ROUNDING_METHOD);
+ expectedValue = SEVEN.divide(THIRTY_ONE, 2 * NUMBER_OF_DECIMALS, ROUNDING_METHOD);
expectedValue = expectedValue.add(ONE);
- expectedValue = expectedValue.add(THREE.divide(THIRTY_ONE, NUMBER_OF_DECIMALS, ROUNDING_METHOD));
+ expectedValue = expectedValue.add(THREE.divide(THIRTY_ONE, 2 * NUMBER_OF_DECIMALS, ROUNDING_METHOD));
+ expectedValue = expectedValue.setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD);
testCalculateNumberOfBillingCycles(startDate, planChangeDate, targetDate, 7, expectedValue);
expectedValue = FIVE.divide(TWENTY_EIGHT, NUMBER_OF_DECIMALS, ROUNDING_METHOD).add(TWO);
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 50f1b23..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
@@ -18,10 +18,21 @@ package com.ning.billing.invoice.tests;
import java.math.BigDecimal;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.catalog.api.BillingPeriod;
+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.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.billing.BillingEvent;
+import com.ning.billing.entitlement.api.billing.BillingModeType;
+import com.ning.billing.entitlement.api.user.Subscription;
import org.joda.time.DateTime;
import com.ning.billing.invoice.model.InvoicingConfiguration;
+import javax.annotation.Nullable;
+
public abstract class InvoicingTestBase {
protected static final int NUMBER_OF_DECIMALS = InvoicingConfiguration.getNumberOfDecimals();
protected static final int ROUNDING_METHOD = InvoicingConfiguration.getRoundingMode();
@@ -32,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);
@@ -49,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);
@@ -70,4 +82,84 @@ public abstract class InvoicingTestBase {
protected DateTime buildDateTime(int year, int month, int day) {
return new DateTime(year, month, day, 0, 0, 0, 0);
}
+
+ protected BillingEvent createMockBillingEvent(@Nullable final Account account, final Subscription subscription,
+ final DateTime effectiveDate,
+ final Plan plan, final PlanPhase planPhase,
+ @Nullable final BigDecimal fixedPrice, @Nullable final BigDecimal recurringPrice,
+ final Currency currency, final BillingPeriod billingPeriod, final int billCycleDay,
+ final BillingModeType billingModeType, final String description,
+ final long totalOrdering,
+ final SubscriptionTransitionType type) {
+ return new BillingEvent() {
+ @Override
+ public Account getAccount() {
+ return account;
+ }
+ @Override
+ public int getBillCycleDay() {
+ return billCycleDay;
+ }
+ @Override
+ public Subscription getSubscription() {
+ return subscription;
+ }
+ @Override
+ public DateTime getEffectiveDate() {
+ return effectiveDate;
+ }
+ @Override
+ public PlanPhase getPlanPhase() {
+ return planPhase;
+ }
+ @Override
+ public Plan getPlan() {
+ return plan;
+ }
+ @Override
+ public BillingPeriod getBillingPeriod() {
+ return billingPeriod;
+ }
+ @Override
+ public BillingModeType getBillingMode() {
+ return billingModeType;
+ }
+ @Override
+ public String getDescription() {
+ return description;
+ }
+ @Override
+ public BigDecimal getFixedPrice() {
+ return fixedPrice;
+ }
+ @Override
+ public BigDecimal getRecurringPrice() {
+ return recurringPrice;
+ }
+ @Override
+ public Currency getCurrency() {
+ return currency;
+ }
+ @Override
+ public SubscriptionTransitionType getTransitionType() {
+ return type;
+ }
+ @Override
+ public Long getTotalOrdering() {
+ return totalOrdering;
+ }
+ @Override
+ public int compareTo(BillingEvent e1) {
+ if (!getSubscription().getId().equals(e1.getSubscription().getId())) { // First order by subscription
+ return getSubscription().getId().compareTo(e1.getSubscription().getId());
+ } else { // subscriptions are the same
+ if (! getEffectiveDate().equals(e1.getEffectiveDate())) { // Secondly order by date
+ return getEffectiveDate().compareTo(e1.getEffectiveDate());
+ } else { // dates and subscriptions are the same
+ return getTotalOrdering().compareTo(e1.getTotalOrdering());
+ }
+ }
+ }
+ };
+ }
}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountJson.java
index 4401b06..2b7e409 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountJson.java
@@ -104,6 +104,17 @@ public class AccountJson extends AccountJsonSimple {
public String getPhone() {
return phone;
}
+
+ @Override
+ public boolean isMigrated() {
+ return false;
+ }
+
+ @Override
+ public boolean isNotifiedForInvoices() {
+ return false;
+ }
+
@Override
public String getPaymentProviderName() {
return paymentProvider;
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingAccount.java b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingAccount.java
index 7f23f84..30347a4 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingAccount.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingAccount.java
@@ -19,6 +19,7 @@ package com.ning.billing.junction.plumbing.api;
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;
@@ -46,6 +47,11 @@ public class BlockingAccount implements Account {
return account.getTagList();
}
+ @Override
+ public boolean hasTag(TagDefinition tagDefinition) {
+ return account.hasTag(tagDefinition);
+ }
+
public String getUpdatedBy() {
return account.getUpdatedBy();
}
@@ -58,8 +64,9 @@ public class BlockingAccount implements Account {
return account.getCreatedBy();
}
- public boolean hasTag(String tagName) {
- return account.hasTag(tagName);
+ @Override
+ public boolean hasTag(ControlTagType controlTagType) {
+ return account.hasTag(controlTagType);
}
public DateTime getUpdatedDate() {
@@ -90,6 +97,11 @@ public class BlockingAccount implements Account {
account.addTags(tags);
}
+ @Override
+ public void addTagsFromDefinitions(List<TagDefinition> tagDefinitions) {
+ account.addTagsFromDefinitions(tagDefinitions);
+ }
+
public void setFieldValue(String fieldName, String fieldValue) {
account.setFieldValue(fieldName, fieldValue);
}
@@ -209,4 +221,14 @@ public class BlockingAccount implements Account {
return account.getPhone();
}
+ @Override
+ public boolean isMigrated() {
+ return account.isMigrated();
+ }
+
+ @Override
+ public boolean isNotifiedForInvoices() {
+ return account.isNotifiedForInvoices();
+ }
+
}
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 38af49f..45e3d5c 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
@@ -23,13 +23,14 @@ import com.google.inject.Inject;
import com.ning.billing.account.api.Account;
import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.account.api.AccountData;
+import com.ning.billing.account.api.AccountEmail;
import com.ning.billing.account.api.AccountUserApi;
import com.ning.billing.account.api.MigrationAccountData;
import com.ning.billing.junction.api.BlockingApi;
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;
@@ -42,15 +43,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
@@ -88,4 +89,14 @@ public class BlockingAccountUserApi implements AccountUserApi {
return userApi.getIdFromKey(externalKey);
}
+ @Override
+ public List<AccountEmail> getEmails(UUID accountId) {
+ return userApi.getEmails(accountId);
+ }
+
+ @Override
+ public void saveEmails(UUID accountId, List<AccountEmail> emails, CallContext context) {
+ userApi.saveEmails(accountId, emails, context);
+ }
+
}
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscription.java b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscription.java
index a3ded85..bc38db9 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscription.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscription.java
@@ -19,6 +19,7 @@ package com.ning.billing.junction.plumbing.api;
import java.util.List;
import java.util.UUID;
+import com.ning.billing.util.tag.ControlTagType;
import org.joda.time.DateTime;
import com.ning.billing.catalog.api.BillingPeriod;
@@ -57,6 +58,11 @@ public class BlockingSubscription implements Subscription {
return subscription.getTagList();
}
+ @Override
+ public boolean hasTag(TagDefinition tagDefinition) {
+ return subscription.hasTag(tagDefinition);
+ }
+
public UUID getId() {
return subscription.getId();
}
@@ -65,8 +71,8 @@ public class BlockingSubscription implements Subscription {
return subscription.getCreatedBy();
}
- public boolean hasTag(String tagName) {
- return subscription.hasTag(tagName);
+ public boolean hasTag(ControlTagType controlTagType) {
+ return subscription.hasTag(controlTagType);
}
public DateTime getCreatedDate() {
@@ -85,6 +91,11 @@ public class BlockingSubscription implements Subscription {
subscription.addTags(tags);
}
+ @Override
+ public void addTagsFromDefinitions(List<TagDefinition> tagDefinitions) {
+ subscription.addTagsFromDefinitions(tagDefinitions);
+ }
+
public void setFieldValue(String fieldName, String fieldValue) {
subscription.setFieldValue(fieldName, fieldValue);
}
diff --git a/junction/src/test/java/com/ning/billing/junction/dao/TestBlockingDao.java b/junction/src/test/java/com/ning/billing/junction/dao/TestBlockingDao.java
index 3fc73ec..0c3b8c8 100644
--- a/junction/src/test/java/com/ning/billing/junction/dao/TestBlockingDao.java
+++ b/junction/src/test/java/com/ning/billing/junction/dao/TestBlockingDao.java
@@ -65,10 +65,11 @@ public class TestBlockingDao {
@AfterClass(groups = "slow")
public void stopMysql()
{
- helper.stopMysql();
+ if (helper != null) {
+ helper.stopMysql();
+ }
}
-
-
+
@Test(groups={"slow"}, enabled=true)
public void testDao() {
ClockMock clock = new ClockMock();
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultEntitlementBillingApi.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultEntitlementBillingApi.java
index bb5e1bb..895eb32 100644
--- a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultEntitlementBillingApi.java
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultEntitlementBillingApi.java
@@ -148,13 +148,13 @@ public class TestDefaultEntitlementBillingApi {
private Subscription subscription;
private DateTime subscriptionStartDate;
- @BeforeSuite(alwaysRun=true)
+ @BeforeSuite(groups={"fast", "slow"})
public void setup() throws ServiceException {
catalogService = new MockCatalogService(new MockCatalog());
clock = new ClockMock();
}
- @BeforeMethod(alwaysRun=true)
+ @BeforeMethod(groups={"fast", "slow"})
public void setupEveryTime() {
bundles = new ArrayList<SubscriptionBundle>();
final SubscriptionBundle bundle = new SubscriptionBundleData( eventId,"TestKey", subId, clock.getUTCNow().minusDays(4), null);
diff --git a/overdue/src/main/java/com/ning/billing/overdue/config/DefaultDuration.java b/overdue/src/main/java/com/ning/billing/overdue/config/DefaultDuration.java
index 90d3670..7301e46 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/config/DefaultDuration.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/config/DefaultDuration.java
@@ -21,6 +21,7 @@ import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import org.joda.time.DateTime;
+import org.joda.time.Period;
import com.ning.billing.catalog.api.Duration;
import com.ning.billing.catalog.api.TimeUnit;
@@ -53,7 +54,7 @@ public class DefaultDuration extends ValidatingConfig<OverdueConfig> implements
@Override
public DateTime addToDateTime(DateTime dateTime) {
- if (number == null) {return dateTime;}
+ if ((number == null) && (unit != TimeUnit.UNLIMITED)) {return dateTime;}
switch (unit) {
case DAYS:
@@ -70,6 +71,24 @@ public class DefaultDuration extends ValidatingConfig<OverdueConfig> implements
}
@Override
+ public Period toJodaPeriod() {
+ if ((number == null) && (unit != TimeUnit.UNLIMITED)) {return new Period();}
+
+ switch (unit) {
+ case DAYS:
+ return new Period().withDays(number);
+ case MONTHS:
+ return new Period().withMonths(number);
+ case YEARS:
+ return new Period().withYears(number);
+ case UNLIMITED:
+ return new Period().withYears(100);
+ default:
+ return new Period();
+ }
+ }
+
+ @Override
public ValidationErrors validate(OverdueConfig catalog, ValidationErrors errors) {
//TODO MDW - Validation TimeUnit UNLIMITED iff number == -1
return errors;
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/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
index cc3db6d..b7b861a 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
@@ -108,7 +108,7 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, CloseMe, Tr
public static final class PaymentAttemptBinder extends BinderBase implements Binder<Bind, PaymentAttempt> {
@Override
public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, PaymentAttempt paymentAttempt) {
- stmt.bind("payment_attempt_id", paymentAttempt.getPaymentAttemptId().toString());
+ stmt.bind("payment_attempt_id", paymentAttempt.getPaymentAttemptId() == null ? null : paymentAttempt.getPaymentAttemptId().toString());
stmt.bind("invoice_id", paymentAttempt.getInvoiceId().toString());
stmt.bind("account_id", paymentAttempt.getAccountId().toString());
stmt.bind("amount", paymentAttempt.getAmount());
diff --git a/payment/src/main/java/com/ning/billing/payment/RetryService.java b/payment/src/main/java/com/ning/billing/payment/RetryService.java
index 7558fbe..447bb74 100644
--- a/payment/src/main/java/com/ning/billing/payment/RetryService.java
+++ b/payment/src/main/java/com/ning/billing/payment/RetryService.java
@@ -100,7 +100,10 @@ public class RetryService implements KillbillService {
return id;
}
};
- retryQueue.recordFutureNotification(timeOfRetry, key);
+
+ if (retryQueue != null) {
+ retryQueue.recordFutureNotification(timeOfRetry, key);
+ }
}
private void retry(String paymentAttemptId, CallContext context) {
diff --git a/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithMocks.java b/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithMocks.java
index 17383ee..8ec8862 100644
--- a/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithMocks.java
+++ b/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithMocks.java
@@ -16,19 +16,16 @@
package com.ning.billing.payment.setup;
+import com.ning.billing.invoice.api.test.DefaultInvoiceTestApi;
+import com.ning.billing.invoice.api.test.InvoiceTestApi;
import org.apache.commons.collections.MapUtils;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Provider;
-import com.ning.billing.account.api.AccountUserApi;
-import com.ning.billing.account.api.user.DefaultAccountUserApi;
-import com.ning.billing.account.dao.AccountDao;
-import com.ning.billing.account.dao.MockAccountDao;
import com.ning.billing.config.PaymentConfig;
-import com.ning.billing.invoice.dao.InvoiceDao;
-import com.ning.billing.invoice.dao.MockInvoiceDao;
import com.ning.billing.junction.api.BillingApi;
import com.ning.billing.mock.BrainDeadProxyFactory;
+import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
import com.ning.billing.payment.dao.MockPaymentDao;
import com.ning.billing.payment.dao.PaymentDao;
import com.ning.billing.payment.provider.MockPaymentProviderPluginModule;
@@ -49,7 +46,7 @@ public class PaymentTestModuleWithMocks extends PaymentModule {
public PaymentTestModuleWithMocks() {
super(MapUtils.toProperties(ImmutableMap.of("killbill.payment.provider.default", "my-mock",
- "killbill.payment.engine.events.off", "false")));
+ "killbill.payment.engine.events.off", "false")));
}
@Override
@@ -66,9 +63,8 @@ public class PaymentTestModuleWithMocks extends PaymentModule {
protected void configure() {
super.configure();
bind(Bus.class).to(InMemoryBus.class).asEagerSingleton();
+ bind(InvoiceTestApi.class).to(DefaultInvoiceTestApi.class).asEagerSingleton();
- bind(MockInvoiceDao.class).asEagerSingleton();
- bind(InvoiceDao.class).to(MockInvoiceDao.class);
bind(NotificationQueueService.class).to(MockNotificationQueueService.class).asEagerSingleton();
}
}
diff --git a/payment/src/test/java/com/ning/billing/payment/TestHelper.java b/payment/src/test/java/com/ning/billing/payment/TestHelper.java
index f856de7..2939c05 100644
--- a/payment/src/test/java/com/ning/billing/payment/TestHelper.java
+++ b/payment/src/test/java/com/ning/billing/payment/TestHelper.java
@@ -19,6 +19,7 @@ package com.ning.billing.payment;
import java.math.BigDecimal;
import java.util.UUID;
+import com.ning.billing.invoice.api.test.InvoiceTestApi;
import org.apache.commons.lang.RandomStringUtils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
@@ -30,7 +31,6 @@ import com.ning.billing.account.api.user.AccountBuilder;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceItem;
-import com.ning.billing.invoice.dao.InvoiceDao;
import com.ning.billing.invoice.model.DefaultInvoice;
import com.ning.billing.invoice.model.RecurringInvoiceItem;
import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
@@ -42,13 +42,13 @@ import com.ning.billing.util.entity.EntityPersistenceException;
public class TestHelper {
protected final AccountUserApi accountUserApi;
- protected final InvoiceDao invoiceDao;
+ protected final InvoiceTestApi invoiceTestApi;
private final CallContext context;
@Inject
- public TestHelper(CallContextFactory factory, AccountUserApi accountUserApi, InvoiceDao invoiceDao) {
+ public TestHelper(CallContextFactory factory, AccountUserApi accountUserApi, InvoiceTestApi invoiceTestApi) {
this.accountUserApi = accountUserApi;
- this.invoiceDao = invoiceDao;
+ this.invoiceTestApi = invoiceTestApi;
context = factory.createCallContext("Princess Buttercup", CallOrigin.TEST, UserType.TEST);
}
@@ -107,7 +107,8 @@ public class TestHelper {
recurringInvoiceItem.getCurrency()));
}
}
- invoiceDao.create(invoice, context);
+
+ invoiceTestApi.create(invoice, context);
return invoice;
}
pom.xml 20(+18 -2)
diff --git a/pom.xml b/pom.xml
index a36879b..02b1aa6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -243,9 +243,19 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-email</artifactId>
+ <version>1.2</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.directory.studio</groupId>
+ <artifactId>org.apache.commons.io</artifactId>
+ <version>2.1</version>
+ </dependency>
+ <dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
- <version>2.0.1</version>
+ <version>2.0</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
@@ -317,6 +327,11 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>com.samskivert</groupId>
+ <artifactId>jmustache</artifactId>
+ <version>1.5</version>
+ </dependency>
+ <dependency>
<groupId>com.jayway.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>1.3.3</version>
@@ -447,7 +462,8 @@
<exclude>**/.settings/**</exclude>
<exclude>.travis.yml</exclude>
<exclude>bin/**</exclude>
-
+ <!-- exclude mustache template files -->
+ <exclude>**/*.mustache</exclude>
</excludes>
</configuration>
</execution>
diff --git a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
index 8de0e2e..e2c37e3 100644
--- a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
+++ b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
@@ -16,6 +16,8 @@
package com.ning.billing.server.modules;
+import com.ning.billing.util.email.EmailModule;
+import com.ning.billing.util.glue.GlobalLockerModule;
import org.skife.jdbi.v2.DBI;
import org.skife.jdbi.v2.IDBI;
@@ -72,6 +74,8 @@ public class KillbillServerModule extends AbstractModule
}
protected void installKillbillModules() {
+ install(new EmailModule());
+ install(new GlobalLockerModule());
install(new FieldStoreModule());
install(new TagStoreModule());
install(new CatalogModule());
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
index 012f88c..73ea829 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
@@ -30,6 +30,8 @@ import java.util.concurrent.TimeUnit;
import javax.ws.rs.core.Response.Status;
+import com.ning.billing.util.email.EmailModule;
+import com.ning.billing.util.glue.GlobalLockerModule;
import org.apache.commons.io.IOUtils;
import org.codehaus.jackson.map.ObjectMapper;
import org.eclipse.jetty.servlet.FilterHolder;
@@ -177,6 +179,8 @@ public class TestJaxrsBase {
super.installKillbillModules();
Modules.override(new com.ning.billing.payment.setup.PaymentModule()).with(new PaymentMockModule());
*/
+ install(new EmailModule());
+ install(new GlobalLockerModule());
install(new FieldStoreModule());
install(new TagStoreModule());
install(new CatalogModule());
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestSubscription.java b/server/src/test/java/com/ning/billing/jaxrs/TestSubscription.java
index d520d49..4a74dfc 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestSubscription.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestSubscription.java
@@ -31,10 +31,8 @@ import org.testng.Assert;
import org.testng.annotations.Test;
import com.ning.billing.catalog.api.BillingPeriod;
-import com.ning.billing.catalog.api.Duration;
import com.ning.billing.catalog.api.PriceListSet;
import com.ning.billing.catalog.api.ProductCategory;
-import com.ning.billing.catalog.api.TimeUnit;
import com.ning.billing.jaxrs.json.AccountJson;
import com.ning.billing.jaxrs.json.BundleJsonNoSubsciptions;
import com.ning.billing.jaxrs.json.SubscriptionJsonNoEvents;
util/pom.xml 16(+16 -0)
diff --git a/util/pom.xml b/util/pom.xml
index f28dcff..580d683 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -73,6 +73,14 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-email</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.directory.studio</groupId>
+ <artifactId>org.apache.commons.io</artifactId>
+ </dependency>
+ <dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<scope>test</scope>
@@ -91,6 +99,14 @@
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>com.samskivert</groupId>
+ <artifactId>jmustache</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ </dependency>
</dependencies>
<build>
<plugins>
diff --git a/util/src/main/java/com/ning/billing/util/customfield/CustomFieldHistoryBinder.java b/util/src/main/java/com/ning/billing/util/customfield/CustomFieldHistoryBinder.java
index 51e3621..5d6f6f2 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/CustomFieldHistoryBinder.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/CustomFieldHistoryBinder.java
@@ -37,7 +37,7 @@ public @interface CustomFieldHistoryBinder {
return new Binder<CustomFieldHistoryBinder, CustomFieldHistory>() {
@Override
public void bind(SQLStatement q, CustomFieldHistoryBinder bind, CustomFieldHistory customFieldHistory) {
- q.bind("historyId", customFieldHistory.getHistoryId().toString());
+ q.bind("historyRecordId", customFieldHistory.getHistoryId().toString());
q.bind("changeType", customFieldHistory.getChangeType().toString());
q.bind("id", customFieldHistory.getId().toString());
q.bind("fieldName", customFieldHistory.getName());
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/dao/AuditedDaoBase.java b/util/src/main/java/com/ning/billing/util/dao/AuditedDaoBase.java
new file mode 100644
index 0000000..03417b4
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/dao/AuditedDaoBase.java
@@ -0,0 +1,31 @@
+/*
+ * 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.util.dao;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+public abstract class AuditedDaoBase {
+ protected List<String> getIdList(int size) {
+ List<String> results = new ArrayList<String>();
+ for (int i = 0; i < size; i++) {
+ results.add(UUID.randomUUID().toString());
+ }
+ return results;
+ }
+}
diff --git a/util/src/main/java/com/ning/billing/util/email/DefaultEmailSender.java b/util/src/main/java/com/ning/billing/util/email/DefaultEmailSender.java
new file mode 100644
index 0000000..b4e3587
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/email/DefaultEmailSender.java
@@ -0,0 +1,68 @@
+/*
+ * 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.util.email;
+
+import java.util.List;
+
+import org.apache.commons.mail.EmailException;
+import org.apache.commons.mail.HtmlEmail;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+
+public class DefaultEmailSender implements EmailSender {
+ private final Logger log = LoggerFactory.getLogger(EmailSender.class);
+ private final EmailConfig config;
+
+ @Inject
+ public DefaultEmailSender(EmailConfig emailConfig) {
+ this.config = emailConfig;
+ }
+
+ @Override
+ public void sendSecureEmail(List<String> to, List<String> cc, String subject, String htmlBody) throws EmailApiException {
+ HtmlEmail email;
+ try {
+ email = new HtmlEmail();
+
+ email.setSmtpPort(config.getSmtpPort());
+ email.setAuthentication(config.getSmtpUserName(), config.getSmtpPassword());
+ email.setHostName(config.getSmtpServerName());
+ email.setFrom(config.getSmtpUserName());
+ email.setSubject(subject);
+ email.setHtmlMsg(htmlBody);
+
+ if (to != null) {
+ for (String recipient : to) {
+ email.addTo(recipient);
+ }
+ }
+
+ if (cc != null) {
+ for (String recipient : cc) {
+ email.addCc(recipient);
+ }
+ }
+
+ email.setSSL(true);
+ email.send();
+ } catch (EmailException ee) {
+ log.warn("Failed to send e-mail", ee);
+ }
+ }
+}
diff --git a/util/src/main/java/com/ning/billing/util/email/EmailConfig.java b/util/src/main/java/com/ning/billing/util/email/EmailConfig.java
new file mode 100644
index 0000000..bcbf5e5
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/email/EmailConfig.java
@@ -0,0 +1,41 @@
+/*
+ * 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.util.email;
+
+import com.ning.billing.config.KillbillConfig;
+import org.skife.config.Config;
+import org.skife.config.Default;
+
+import java.util.Locale;
+
+public interface EmailConfig extends KillbillConfig {
+ @Config("mail.smtp.host")
+ @Default("smtp.gmail.com")
+ public String getSmtpServerName();
+
+ @Config("mail.smtp.port")
+ @Default("465")
+ public int getSmtpPort();
+
+ @Config("mail.smtp.user")
+ @Default("killbill.ning@gmail.com")
+ public String getSmtpUserName();
+
+ @Config("mail.smtp.password")
+ @Default("killbill@ning!")
+ public String getSmtpPassword();
+}
diff --git a/util/src/main/java/com/ning/billing/util/email/EmailModule.java b/util/src/main/java/com/ning/billing/util/email/EmailModule.java
new file mode 100644
index 0000000..5bb252a
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/email/EmailModule.java
@@ -0,0 +1,32 @@
+/*
+ * 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.util.email;
+
+import com.google.inject.AbstractModule;
+import org.skife.config.ConfigurationObjectFactory;
+
+public class EmailModule extends AbstractModule {
+ protected void installEmailConfig() {
+ EmailConfig config = new ConfigurationObjectFactory(System.getProperties()).build(EmailConfig.class);
+ bind(EmailConfig.class).toInstance(config);
+ }
+
+ @Override
+ protected void configure() {
+ installEmailConfig();
+ }
+}
diff --git a/util/src/main/java/com/ning/billing/util/email/templates/MustacheTemplateEngine.java b/util/src/main/java/com/ning/billing/util/email/templates/MustacheTemplateEngine.java
new file mode 100644
index 0000000..dba8757
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/email/templates/MustacheTemplateEngine.java
@@ -0,0 +1,42 @@
+/*
+ * 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.util.email.templates;
+
+import com.samskivert.mustache.Mustache;
+import com.samskivert.mustache.Template;
+import org.apache.commons.io.IOUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.util.Map;
+
+public class MustacheTemplateEngine implements TemplateEngine {
+ @Override
+ public String executeTemplate(String templateName, Map<String, Object> data) throws IOException {
+ String templateText = getTemplateText(templateName);
+ Template template = Mustache.compiler().compile(templateText);
+ return template.execute(data);
+ }
+
+ private String getTemplateText(String templateName) throws IOException {
+ InputStream templateStream = this.getClass().getResourceAsStream(templateName + ".mustache");
+ StringWriter writer = new StringWriter();
+ IOUtils.copy(templateStream, writer, "UTF-8");
+ return writer.toString();
+ }
+}
diff --git a/util/src/main/java/com/ning/billing/util/email/templates/TemplateEngine.java b/util/src/main/java/com/ning/billing/util/email/templates/TemplateEngine.java
new file mode 100644
index 0000000..9a47008
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/email/templates/TemplateEngine.java
@@ -0,0 +1,24 @@
+/*
+ * 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.util.email.templates;
+
+import java.io.IOException;
+import java.util.Map;
+
+public interface TemplateEngine {
+ public String executeTemplate(String templateName, Map<String, Object> data) throws IOException;
+}
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..a52e276 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 tagDefinition) {
+ return tagStore.containsTagForDefinition(tagDefinition);
}
+ @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/glue/RealImplementation.java b/util/src/main/java/com/ning/billing/util/glue/RealImplementation.java
index 4ccd871..d72a698 100644
--- a/util/src/main/java/com/ning/billing/util/glue/RealImplementation.java
+++ b/util/src/main/java/com/ning/billing/util/glue/RealImplementation.java
@@ -30,7 +30,7 @@ import com.google.inject.BindingAnnotation;
* This annotation is used to bing classes that are being intercepted in junction.
*
* The real implementation of the class is bound in Guice with this parameter, the Blocking implementation
- * is let unannotated.
+ * is left unannotated.
*
*/
@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD,LOCAL_VARIABLE }) @Retention(RUNTIME)
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 374e0a3..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
@@ -22,18 +22,18 @@ import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.util.ChangeType;
import com.ning.billing.util.audit.dao.AuditSqlDao;
import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.dao.AuditedDaoBase;
import com.ning.billing.util.tag.Tag;
import org.skife.jdbi.v2.IDBI;
import org.skife.jdbi.v2.Transaction;
import org.skife.jdbi.v2.TransactionStatus;
import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
-public class AuditedTagDao implements TagDao {
+public class AuditedTagDao extends AuditedDaoBase implements TagDao {
private final TagSqlDao tagSqlDao;
@Inject
@@ -52,10 +52,10 @@ public class AuditedTagDao 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 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();
@@ -85,14 +85,6 @@ public class AuditedTagDao implements TagDao {
auditSqlDao.insertAuditFromTransaction("tag_history", historyIdsForDelete, ChangeType.DELETE, context);
}
- private List<String> getIdList(int size) {
- List<String> results = new ArrayList<String>();
- for (int i = 0; i < size; i++) {
- results.add(UUID.randomUUID().toString());
- }
- return results;
- }
-
@Override
public List<Tag> loadTags(final UUID objectId, final String objectType) {
return tagSqlDao.load(objectId.toString(), objectType);
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/java/com/ning/billing/util/template/translation/DefaultCatalogTranslator.java b/util/src/main/java/com/ning/billing/util/template/translation/DefaultCatalogTranslator.java
new file mode 100644
index 0000000..933b24f
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/template/translation/DefaultCatalogTranslator.java
@@ -0,0 +1,36 @@
+/*
+ * 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.util.template.translation;
+
+import com.google.inject.Inject;
+
+public class DefaultCatalogTranslator extends DefaultTranslatorBase {
+ @Inject
+ public DefaultCatalogTranslator(TranslatorConfig config) {
+ super(config);
+ }
+
+ @Override
+ protected String getBundlePath() {
+ return "com/ning/billing/util/template/translation/CatalogTranslation";
+ }
+
+ @Override
+ protected String getTranslationType() {
+ return "catalog";
+ }
+}
diff --git a/util/src/main/java/com/ning/billing/util/template/translation/DefaultTranslatorBase.java b/util/src/main/java/com/ning/billing/util/template/translation/DefaultTranslatorBase.java
new file mode 100644
index 0000000..4d1a7b6
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/template/translation/DefaultTranslatorBase.java
@@ -0,0 +1,71 @@
+/*
+ * 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.util.template.translation;
+
+import com.google.inject.Inject;
+import com.ning.billing.ErrorCode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+public abstract class DefaultTranslatorBase implements Translator {
+ protected final TranslatorConfig config;
+ protected final Logger log = LoggerFactory.getLogger(DefaultTranslatorBase.class);
+
+ @Inject
+ public DefaultTranslatorBase(TranslatorConfig config) {
+ this.config = config;
+ }
+
+ protected abstract String getBundlePath();
+
+ /*
+ * string used for exception handling
+ */
+ protected abstract String getTranslationType();
+
+ @Override
+ public String getTranslation(Locale locale, String originalText) {
+ ResourceBundle bundle = null;
+ try {
+ bundle = ResourceBundle.getBundle(getBundlePath(), locale);
+ } catch (MissingResourceException mrex) {
+ log.warn(String.format(ErrorCode.MISSING_TRANSLATION_RESOURCE.toString(), getTranslationType()));
+ }
+
+ if ((bundle != null) && (bundle.containsKey(originalText))) {
+ return bundle.getString(originalText);
+ } else {
+ try {
+ Locale defaultLocale = new Locale(config.getDefaultLocale());
+ bundle = ResourceBundle.getBundle(getBundlePath(), defaultLocale);
+
+ if ((bundle != null) && (bundle.containsKey(originalText))) {
+ return bundle.getString(originalText);
+ } else {
+ return originalText;
+ }
+ } catch (MissingResourceException mrex) {
+ log.warn(String.format(ErrorCode.MISSING_TRANSLATION_RESOURCE.toString(), getTranslationType()));
+ return originalText;
+ }
+ }
+ }
+}
diff --git a/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldHistorySqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldHistorySqlDao.sql.stg
index c9e2ab0..6e5a43f 100644
--- a/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldHistorySqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldHistorySqlDao.sql.stg
@@ -1,7 +1,7 @@
group CustomFieldHistorySqlDao;
fields(prefix) ::= <<
- <prefix>history_id,
+ <prefix>history_record_id,
<prefix>id,
<prefix>object_id,
<prefix>object_type,
@@ -14,5 +14,5 @@ fields(prefix) ::= <<
batchAddHistoryFromTransaction() ::= <<
INSERT INTO custom_field_history(<fields()>)
- VALUES(:historyId, :id, :objectId, :objectType, :fieldName, :fieldValue, :userName, :updatedDate, :changeType);
+ VALUES(:historyRecordId, :id, :objectId, :objectType, :fieldName, :fieldValue, :userName, :updatedDate, :changeType);
>>
\ 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 852dd0b..2162500 100644
--- a/util/src/main/resources/com/ning/billing/util/ddl.sql
+++ b/util/src/main/resources/com/ning/billing/util/ddl.sql
@@ -16,7 +16,7 @@ CREATE UNIQUE INDEX custom_fields_unique ON custom_fields(object_id, object_type
DROP TABLE IF EXISTS custom_field_history;
CREATE TABLE custom_field_history (
- history_id char(36) NOT NULL,
+ history_record_id char(36) NOT NULL,
id char(36) NOT NULL,
object_id char(36) NOT NULL,
object_type varchar(30) NOT NULL,
@@ -119,6 +119,8 @@ 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);
DROP TABLE IF EXISTS bus_events;
CREATE TABLE bus_events (
diff --git a/util/src/main/resources/com/ning/billing/util/email/templates/HtmlInvoiceTemplate.mustache b/util/src/main/resources/com/ning/billing/util/email/templates/HtmlInvoiceTemplate.mustache
new file mode 100644
index 0000000..73c70b3
--- /dev/null
+++ b/util/src/main/resources/com/ning/billing/util/email/templates/HtmlInvoiceTemplate.mustache
@@ -0,0 +1,96 @@
+<html>
+ <head>
+ <style type="text/css">
+ th {align=left; width=225px; border-bottom: solid 2px black;}
+ </style>
+ </head>
+ <body>
+ <h1>{{text.invoiceTitle}}</h1>
+ <table>
+ <tr>
+ <td rowspan=3 width=350px>Insert image here</td>
+ <td width=100px/>
+ <td width=225px/>
+ <td width=225px/>
+ </tr>
+ <tr>
+ <td />
+ <td align=right>{{text.invoiceDate}}</td>
+ <td>{{invoice.formattedInvoiceDate}}</td>
+ </tr>
+ <tr>
+ <td />
+ <td align=right>{{text.invoiceNumber}}</td>
+ <td>{{invoice.invoiceNumber}}</td>
+ </tr>
+ <tr>
+ <td>{{text.companyName}}</td>
+ <td></td>
+ <td align=right>{{text.accountOwnerName}}</td>
+ <td>{{account.name}}</td>
+ </tr>
+ <tr>
+ <td>{{text.companyAddress}}</td>
+ <td />
+ <td />
+ <td>{{account.email}}</td>
+ </tr>
+ <tr>
+ <td>{{text.companyCityProvincePostalCode}}</td>
+ <td />
+ <td />
+ <td>{{account.phone}}</td>
+ </tr>
+ <tr>
+ <td>{{text.companyCountry}}</td>
+ <td />
+ <td />
+ <td />
+ </tr>
+ <tr>
+ <td><{{text.companyUrl}}</td>
+ <td />
+ <td />
+ <td />
+ </tr>
+ </table>
+ <br />
+ <br />
+ <br />
+ <table>
+ <tr>
+ <th>{{text.invoiceItemBundleName}}</td>
+ <th>{{text.invoiceItemDescription}}</td>
+ <th>{{text.invoiceItemServicePeriod}}</td>
+ <th>{{text.invoiceItemAmount}}</td>
+ </tr>
+ {{#invoice.invoiceItems}}
+ <tr>
+ <td>{{description}}</td>
+ <td>{{planName}}</td>
+ <td>{{formattedStartDate}} - {{formattedEndDate}}</td>
+ <td>{{invoice.currency}} {{amount}}</td>
+ </tr>
+ {{/invoice.invoiceItems}}
+ <tr>
+ <td colspan=4 />
+ </tr>
+ <tr>
+ <td colspan=2 />
+ <td align=right><strong>{{text.invoiceAmount}}</strong></td>
+ <td align=right><strong>{{invoice.totalAmount}}</strong></td>
+ </tr>
+ <tr>
+ <td colspan=2 />
+ <td align=right><strong>{{text.invoiceAmountPaid}}</strong></td>
+ <td align=right><strong>{{invoice.amountPaid}}</strong></td>
+ </tr>
+ <tr>
+ <td colspan=2 />
+ <td align=right><strong>{{text.invoiceBalance}}</strong></td>
+ <td align=right><strong>{{invoice.balance}}</strong></td>
+ </tr>
+ </table>
+ </body>
+</html>
+
diff --git a/util/src/main/resources/com/ning/billing/util/template/translation/CatalogTranslation_EN_US.properties b/util/src/main/resources/com/ning/billing/util/template/translation/CatalogTranslation_EN_US.properties
new file mode 100644
index 0000000..b05a595
--- /dev/null
+++ b/util/src/main/resources/com/ning/billing/util/template/translation/CatalogTranslation_EN_US.properties
@@ -0,0 +1,2 @@
+ning-pro = Pro
+ning-plus = Plus
\ No newline at end of file
diff --git a/util/src/main/resources/com/ning/billing/util/template/translation/CatalogTranslation_FR_CA.properties b/util/src/main/resources/com/ning/billing/util/template/translation/CatalogTranslation_FR_CA.properties
new file mode 100644
index 0000000..e025a88
--- /dev/null
+++ b/util/src/main/resources/com/ning/billing/util/template/translation/CatalogTranslation_FR_CA.properties
@@ -0,0 +1 @@
+ning-plus = Plus en francais
\ No newline at end of file
diff --git a/util/src/main/resources/com/ning/billing/util/template/translation/InvoiceTranslation_EN_US.properties b/util/src/main/resources/com/ning/billing/util/template/translation/InvoiceTranslation_EN_US.properties
new file mode 100644
index 0000000..0162671
--- /dev/null
+++ b/util/src/main/resources/com/ning/billing/util/template/translation/InvoiceTranslation_EN_US.properties
@@ -0,0 +1,20 @@
+invoiceTitle=INVOICE
+invoiceDate=Date:
+invoiceNumber=Invoice #
+invoiceAmount=New Charges
+invoiceAmountPaid=Payment
+invoiceBalance=Balance
+
+accountOwnerName=Network Creator
+
+companyName=Ning, Inc.
+companyAddress=P.O. Box 1622
+companyCityProvincePostalCode=Palo Alto, CA 94302
+companyCountry=USA
+companyUrl=http://www.ning.com
+
+invoiceItemBundleName=NetworkName
+invoiceItemDescription=Description
+invoiceItemServicePeriod=Service Period
+invoiceItemAmount=Amount
+
diff --git a/util/src/test/java/com/ning/billing/util/customfield/TestFieldStore.java b/util/src/test/java/com/ning/billing/util/customfield/TestFieldStore.java
index 8a2385b..fbd2be3 100644
--- a/util/src/test/java/com/ning/billing/util/customfield/TestFieldStore.java
+++ b/util/src/test/java/com/ning/billing/util/customfield/TestFieldStore.java
@@ -79,7 +79,9 @@ public class TestFieldStore {
@AfterClass(groups = {"util", "slow"})
public void stopMysql()
{
- helper.stopMysql();
+ if (helper!= null) {
+ helper.stopMysql();
+ }
}
@Test
diff --git a/util/src/test/java/com/ning/billing/util/email/DefaultCatalogTranslationTest.java b/util/src/test/java/com/ning/billing/util/email/DefaultCatalogTranslationTest.java
new file mode 100644
index 0000000..0be05ac
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/email/DefaultCatalogTranslationTest.java
@@ -0,0 +1,84 @@
+package com.ning.billing.util.email;/*
+ * 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.
+ */
+
+import com.ning.billing.util.template.translation.DefaultCatalogTranslator;
+import com.ning.billing.util.template.translation.Translator;
+import com.ning.billing.util.template.translation.TranslatorConfig;
+import org.skife.config.ConfigurationObjectFactory;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.util.Locale;
+
+import static org.testng.Assert.assertEquals;
+
+@Test(groups = {"fast", "email"})
+public class DefaultCatalogTranslationTest {
+ private Translator translation;
+
+ @BeforeClass(groups={"fast", "email"})
+ public void setup() {
+ final TranslatorConfig config = new ConfigurationObjectFactory(System.getProperties()).build(TranslatorConfig.class);
+ translation = new DefaultCatalogTranslator(config);
+ }
+
+ @Test(groups = {"fast", "email"})
+ public void testInitialization() {
+ String ningPlusText = "ning-plus";
+ String ningProText = "ning-pro";
+ String badText = "Bad text";
+
+ assertEquals(translation.getTranslation(Locale.US, ningPlusText), "Plus");
+ assertEquals(translation.getTranslation(Locale.US, ningProText), "Pro");
+ assertEquals(translation.getTranslation(Locale.US, badText), badText);
+
+ assertEquals(translation.getTranslation(Locale.CANADA_FRENCH, ningPlusText), "Plus en francais");
+ assertEquals(translation.getTranslation(Locale.CANADA_FRENCH, ningProText), "Pro");
+ assertEquals(translation.getTranslation(Locale.CANADA_FRENCH, badText), badText);
+
+ assertEquals(translation.getTranslation(Locale.CHINA, ningPlusText), "Plus");
+ assertEquals(translation.getTranslation(Locale.CHINA, ningProText), "Pro");
+ assertEquals(translation.getTranslation(Locale.CHINA, badText), badText);
+ }
+
+ @Test
+ public void testExistingTranslation() {
+ // if the translation exists, return the translation
+ String originalText = "ning-plus";
+ assertEquals(translation.getTranslation(Locale.US, originalText), "Plus");
+ }
+
+ @Test
+ public void testMissingTranslation() {
+ // if the translation is missing from the file, return the original text
+ String originalText = "missing translation";
+ assertEquals(translation.getTranslation(Locale.US, originalText), originalText);
+ }
+
+ @Test
+ public void testMissingTranslationFileWithEnglishText() {
+ // if the translation file doesn't exist, return the "English" translation
+ String originalText = "ning-plus";
+ assertEquals(translation.getTranslation(Locale.CHINA, originalText), "Plus");
+ }
+
+ @Test
+ public void testMissingFileAndText() {
+ // if the file is missing, and the "English" translation is missing, return the original text
+ String originalText = "missing translation";
+ assertEquals(translation.getTranslation(Locale.CHINA, originalText), originalText);
+ }
+}
diff --git a/util/src/test/java/com/ning/billing/util/email/EmailSenderTest.java b/util/src/test/java/com/ning/billing/util/email/EmailSenderTest.java
new file mode 100644
index 0000000..1685628
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/email/EmailSenderTest.java
@@ -0,0 +1,42 @@
+package com.ning.billing.util.email;/*
+ * 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.
+ */
+
+import org.skife.config.ConfigurationObjectFactory;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Test(groups = {"slow", "email"})
+public class EmailSenderTest {
+ private EmailConfig config;
+
+ @BeforeClass
+ public void setup() {
+ config = new ConfigurationObjectFactory(System.getProperties()).build(EmailConfig.class);
+ }
+
+ @Test
+ public void testSendEmail() throws Exception {
+ String html = "<html><body><h1>Test E-mail</h1></body></html>";
+ List<String> recipients = new ArrayList<String>();
+ recipients.add("killbill.ning@gmail.com");
+
+ EmailSender sender = new DefaultEmailSender(config);
+ sender.sendSecureEmail(recipients, null, "Test message", html);
+ }
+}
diff --git a/util/src/test/java/com/ning/billing/util/globallocker/TestMysqlGlobalLocker.java b/util/src/test/java/com/ning/billing/util/globallocker/TestMysqlGlobalLocker.java
index 6f2facd..be642f8 100644
--- a/util/src/test/java/com/ning/billing/util/globallocker/TestMysqlGlobalLocker.java
+++ b/util/src/test/java/com/ning/billing/util/globallocker/TestMysqlGlobalLocker.java
@@ -58,7 +58,9 @@ public class TestMysqlGlobalLocker {
@AfterClass(groups = "slow")
public void tearDown() {
- helper.stopMysql();
+ if (helper != null) {
+ helper.stopMysql();
+ }
}
// Used as a manual test to validate the simple DAO by stepping through that locking is done and release correctly
diff --git a/util/src/test/java/com/ning/billing/util/notificationq/dao/TestNotificationSqlDao.java b/util/src/test/java/com/ning/billing/util/notificationq/dao/TestNotificationSqlDao.java
index 891cadf..d5de415 100644
--- a/util/src/test/java/com/ning/billing/util/notificationq/dao/TestNotificationSqlDao.java
+++ b/util/src/test/java/com/ning/billing/util/notificationq/dao/TestNotificationSqlDao.java
@@ -78,7 +78,9 @@ public class TestNotificationSqlDao {
@AfterSuite(groups = "slow")
public void stopMysql()
{
- helper.stopMysql();
+ if (helper != null) {
+ helper.stopMysql();
+ }
}
diff --git a/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java b/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java
index 1a9654c..80f4a5c 100644
--- a/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java
+++ b/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java
@@ -92,7 +92,9 @@ public class TestNotificationQueue {
@AfterClass(groups="slow")
public void tearDown() {
- helper.stopMysql();
+ if (helper != null) {
+ helper.stopMysql();
+ }
}
@BeforeTest(groups="slow")
@@ -131,7 +133,7 @@ public class TestNotificationQueue {
synchronized (expectedNotifications) {
log.info("Handler received key: " + notificationKey);
- expectedNotifications.put(notificationKey.toString(), Boolean.TRUE);
+ expectedNotifications.put(notificationKey, Boolean.TRUE);
expectedNotifications.notify();
}
}
@@ -432,7 +434,7 @@ public class TestNotificationQueue {
new NotificationQueueHandler() {
@Override
public void handleReadyNotification(String key, DateTime eventDateTime) {
- if(key.equals(notificationKey) || key.equals(notificationKey2)) { //ig nore stray events from other tests
+ if(key.equals(notificationKey) || key.equals(notificationKey2)) { //ignore stray events from other tests
log.info("Received notification with key: " + notificationKey);
eventsReceived++;
}
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 a8d353f..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
@@ -103,7 +103,9 @@ public class TestTagStore {
@AfterClass(groups="slow")
public void stopMysql()
{
- helper.stopMysql();
+ if (helper != null) {
+ helper.stopMysql();
+ }
}
private void cleanupTags() {
@@ -113,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;
}
@@ -307,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 {
@@ -332,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 {
@@ -391,6 +393,8 @@ public class TestTagStore {
String query = String.format("select * from audit_log a inner join tag_history th on a.record_id = th.history_record_id where a.table_name = 'tag_history' and th.id='%s' and a.change_type='INSERT'",
tag.getId().toString());
List<Map<String, Object>> result = handle.select(query);
+ handle.close();
+
assertNotNull(result);
assertEquals(result.size(), 1);
assertEquals(result.get(0).get("change_type"), "INSERT");
@@ -420,6 +424,8 @@ public class TestTagStore {
String query = String.format("select * from audit_log a inner join tag_history th on a.record_id = th.history_record_id where a.table_name = 'tag_history' and th.id='%s' and a.change_type='DELETE'",
tag.getId().toString());
List<Map<String, Object>> result = handle.select(query);
+ handle.close();
+
assertNotNull(result);
assertEquals(result.size(), 1);
assertNotNull(result.get(0).get("change_date"));
diff --git a/util/src/test/java/com/ning/billing/util/validation/TestValidationManager.java b/util/src/test/java/com/ning/billing/util/validation/TestValidationManager.java
index 1848950..4e845db 100644
--- a/util/src/test/java/com/ning/billing/util/validation/TestValidationManager.java
+++ b/util/src/test/java/com/ning/billing/util/validation/TestValidationManager.java
@@ -67,7 +67,9 @@ public class TestValidationManager {
}
private void stopDatabase() {
- helper.stopMysql();
+ if (helper != null) {
+ helper.stopMysql();
+ }
}
@Test(groups = "slow")