killbill-memoizeit

account: allow null values for name and email This fixes https://github.com/killbill/killbill/issues/270. This

7/13/2015 7:44:57 AM

Details

diff --git a/account/src/main/java/org/killbill/billing/account/api/DefaultAccount.java b/account/src/main/java/org/killbill/billing/account/api/DefaultAccount.java
index 7f6f246..25384b3 100644
--- a/account/src/main/java/org/killbill/billing/account/api/DefaultAccount.java
+++ b/account/src/main/java/org/killbill/billing/account/api/DefaultAccount.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -22,27 +24,14 @@ import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
-
 import org.killbill.billing.account.dao.AccountModelDao;
 import org.killbill.billing.catalog.api.Currency;
-import org.killbill.billing.entitlement.api.BlockingState;
 import org.killbill.billing.entity.EntityBase;
 
-import com.google.common.base.Objects;
-import com.google.common.base.Optional;
+import static org.killbill.billing.account.api.DefaultMutableAccountData.DEFAULT_BILLING_CYCLE_DAY_LOCAL;
 
 public class DefaultAccount extends EntityBase implements Account {
 
-    // Default values. When updating an account object, null values are
-    // interpreted as "no change". You can use these defaults to reset
-    // some fields
-    public static final String DEFAULT_STRING_VALUE = "";
-    public static final Integer DEFAULT_INTEGER_VALUE = 0;
-    public static final Currency DEFAULT_CURRENCY_VALUE = Currency.USD;
-    public static final DateTimeZone DEFAULT_TIMEZONE_VALUE = DateTimeZone.UTC;
-    public static final Boolean DEFAULT_MIGRATED_VALUE = false;
-    public static final Boolean DEFAULT_NOTIFIED_FOR_INVOICES_VALUE = false;
-
     private final String externalKey;
     private final String email;
     private final String name;
@@ -70,17 +59,29 @@ public class DefaultAccount extends EntityBase implements Account {
      * @param data AccountData new data for the existing account
      */
     public DefaultAccount(final UUID id, final AccountData data) {
-        this(id, data.getExternalKey(), data.getEmail(), data.getName(), data.getFirstNameLength(),
-             data.getCurrency(), data.getBillCycleDayLocal(), data.getPaymentMethodId(),
-             data.getTimeZone(), data.getLocale(),
-             data.getAddress1(), data.getAddress2(), data.getCompanyName(),
-             data.getCity(), data.getStateOrProvince(), data.getCountry(),
-             data.getPostalCode(), data.getPhone(), data.isMigrated(), data.isNotifiedForInvoices());
+        this(id,
+             data.getExternalKey(),
+             data.getEmail(),
+             data.getName(),
+             data.getFirstNameLength(),
+             data.getCurrency(),
+             data.getBillCycleDayLocal(),
+             data.getPaymentMethodId(),
+             data.getTimeZone(),
+             data.getLocale(),
+             data.getAddress1(),
+             data.getAddress2(),
+             data.getCompanyName(),
+             data.getCity(),
+             data.getStateOrProvince(),
+             data.getCountry(),
+             data.getPostalCode(),
+             data.getPhone(),
+             data.isMigrated(),
+             data.isNotifiedForInvoices());
     }
 
-    /*
-    * This call is used for testing and update from an existing account
-    */
+    // This call is used for testing and update from an existing account
     public DefaultAccount(final UUID id, final String externalKey, final String email,
                           final String name, final Integer firstNameLength,
                           final Currency currency, final Integer billCycleDayLocal, final UUID paymentMethodId,
@@ -89,9 +90,28 @@ public class DefaultAccount extends EntityBase implements Account {
                           final String city, final String stateOrProvince, final String country,
                           final String postalCode, final String phone,
                           final Boolean isMigrated, final Boolean isNotifiedForInvoices) {
-        this(id, null, null, externalKey, email, name, firstNameLength, currency, billCycleDayLocal, paymentMethodId,
-             timeZone, locale, address1, address2, companyName, city, stateOrProvince, country, postalCode,
-             phone, isMigrated, isNotifiedForInvoices);
+        this(id,
+             null,
+             null,
+             externalKey,
+             email,
+             name,
+             firstNameLength,
+             currency,
+             billCycleDayLocal,
+             paymentMethodId,
+             timeZone,
+             locale,
+             address1,
+             address2,
+             companyName,
+             city,
+             stateOrProvince,
+             country,
+             postalCode,
+             phone,
+             isMigrated,
+             isNotifiedForInvoices);
     }
 
     public DefaultAccount(final UUID id, @Nullable final DateTime createdDate, @Nullable final DateTime updatedDate,
@@ -109,7 +129,7 @@ public class DefaultAccount extends EntityBase implements Account {
         this.name = name;
         this.firstNameLength = firstNameLength;
         this.currency = currency;
-        this.billCycleDayLocal = billCycleDayLocal;
+        this.billCycleDayLocal = billCycleDayLocal == null ? DEFAULT_BILLING_CYCLE_DAY_LOCAL : billCycleDayLocal;
         this.paymentMethodId = paymentMethodId;
         this.timeZone = timeZone;
         this.locale = locale;
@@ -126,42 +146,58 @@ public class DefaultAccount extends EntityBase implements Account {
     }
 
     public DefaultAccount(final AccountModelDao accountModelDao) {
-        this(accountModelDao.getId(), accountModelDao.getCreatedDate(), accountModelDao.getUpdatedDate(), accountModelDao.getExternalKey(),
-             accountModelDao.getEmail(), accountModelDao.getName(), accountModelDao.getFirstNameLength(), accountModelDao.getCurrency(),
-             accountModelDao.getBillingCycleDayLocal(), accountModelDao.getPaymentMethodId(),
-             accountModelDao.getTimeZone(), accountModelDao.getLocale(), accountModelDao.getAddress1(), accountModelDao.getAddress2(),
-             accountModelDao.getCompanyName(), accountModelDao.getCity(), accountModelDao.getStateOrProvince(), accountModelDao.getCountry(),
-             accountModelDao.getPostalCode(), accountModelDao.getPhone(), accountModelDao.getMigrated(), accountModelDao.getIsNotifiedForInvoices());
+        this(accountModelDao.getId(),
+             accountModelDao.getCreatedDate(),
+             accountModelDao.getUpdatedDate(),
+             accountModelDao.getExternalKey(),
+             accountModelDao.getEmail(),
+             accountModelDao.getName(),
+             accountModelDao.getFirstNameLength(),
+             accountModelDao.getCurrency(),
+             accountModelDao.getBillingCycleDayLocal(),
+             accountModelDao.getPaymentMethodId(),
+             accountModelDao.getTimeZone(),
+             accountModelDao.getLocale(),
+             accountModelDao.getAddress1(),
+             accountModelDao.getAddress2(),
+             accountModelDao.getCompanyName(),
+             accountModelDao.getCity(),
+             accountModelDao.getStateOrProvince(),
+             accountModelDao.getCountry(),
+             accountModelDao.getPostalCode(),
+             accountModelDao.getPhone(),
+             accountModelDao.getMigrated(),
+             accountModelDao.getIsNotifiedForInvoices());
     }
 
     @Override
     public String getExternalKey() {
-        return Objects.firstNonNull(externalKey, DEFAULT_STRING_VALUE);
+        return externalKey;
     }
 
     @Override
     public String getName() {
-        return Objects.firstNonNull(name, DEFAULT_STRING_VALUE);
+        return name;
     }
 
     @Override
     public String getEmail() {
-        return Objects.firstNonNull(email, DEFAULT_STRING_VALUE);
+        return email;
     }
 
     @Override
     public Integer getFirstNameLength() {
-        return Objects.firstNonNull(firstNameLength, DEFAULT_INTEGER_VALUE);
+        return firstNameLength;
     }
 
     @Override
     public Currency getCurrency() {
-        return Objects.firstNonNull(currency, DEFAULT_CURRENCY_VALUE);
+        return currency;
     }
 
     @Override
     public Integer getBillCycleDayLocal() {
-        return Objects.firstNonNull(billCycleDayLocal, DEFAULT_INTEGER_VALUE);
+        return billCycleDayLocal;
     }
 
     @Override
@@ -172,62 +208,62 @@ public class DefaultAccount extends EntityBase implements Account {
 
     @Override
     public DateTimeZone getTimeZone() {
-        return Objects.firstNonNull(timeZone, DEFAULT_TIMEZONE_VALUE);
+        return timeZone;
     }
 
     @Override
     public String getLocale() {
-        return Objects.firstNonNull(locale, DEFAULT_STRING_VALUE);
+        return locale;
     }
 
     @Override
     public String getAddress1() {
-        return Objects.firstNonNull(address1, DEFAULT_STRING_VALUE);
+        return address1;
     }
 
     @Override
     public String getAddress2() {
-        return Objects.firstNonNull(address2, DEFAULT_STRING_VALUE);
+        return address2;
     }
 
     @Override
     public String getCompanyName() {
-        return Objects.firstNonNull(companyName, DEFAULT_STRING_VALUE);
+        return companyName;
     }
 
     @Override
     public String getCity() {
-        return Objects.firstNonNull(city, DEFAULT_STRING_VALUE);
+        return city;
     }
 
     @Override
     public String getStateOrProvince() {
-        return Objects.firstNonNull(stateOrProvince, DEFAULT_STRING_VALUE);
+        return stateOrProvince;
     }
 
     @Override
     public String getPostalCode() {
-        return Objects.firstNonNull(postalCode, DEFAULT_STRING_VALUE);
+        return postalCode;
     }
 
     @Override
     public String getCountry() {
-        return Objects.firstNonNull(country, DEFAULT_STRING_VALUE);
+        return country;
     }
 
     @Override
     public Boolean isMigrated() {
-        return Objects.firstNonNull(this.isMigrated, DEFAULT_MIGRATED_VALUE);
+        return isMigrated;
     }
 
     @Override
     public Boolean isNotifiedForInvoices() {
-        return Objects.firstNonNull(isNotifiedForInvoices, DEFAULT_NOTIFIED_FOR_INVOICES_VALUE);
+        return isNotifiedForInvoices;
     }
 
     @Override
     public String getPhone() {
-        return Objects.firstNonNull(phone, DEFAULT_STRING_VALUE);
+        return phone;
     }
 
     @Override
@@ -259,35 +295,43 @@ public class DefaultAccount extends EntityBase implements Account {
             accountData.setCurrency(currentAccount.getCurrency());
         }
 
-        if (billCycleDayLocal != null && billCycleDayLocal != 0 && currentAccount.getBillCycleDayLocal() != 0 && !billCycleDayLocal.equals(currentAccount.getBillCycleDayLocal())) {
+        if (billCycleDayLocal != null && billCycleDayLocal != DEFAULT_BILLING_CYCLE_DAY_LOCAL && currentAccount.getBillCycleDayLocal() != null && currentAccount.getBillCycleDayLocal() != DEFAULT_BILLING_CYCLE_DAY_LOCAL && !billCycleDayLocal.equals(currentAccount.getBillCycleDayLocal())) {
             throw new IllegalArgumentException(String.format("Killbill doesn't support updating the account BCD yet: new=%s, current=%s",
                                                              billCycleDayLocal, currentAccount.getBillCycleDayLocal()));
-        } else if (billCycleDayLocal != null && billCycleDayLocal != 0) {
+        } else if (billCycleDayLocal != null && billCycleDayLocal != DEFAULT_BILLING_CYCLE_DAY_LOCAL) {
             // Junction sets it
             accountData.setBillCycleDayLocal(billCycleDayLocal);
         } else {
             // Default to current value
-            accountData.setBillCycleDayLocal(currentAccount.getBillCycleDayLocal());
+            accountData.setBillCycleDayLocal(currentAccount.getBillCycleDayLocal() == null ? DEFAULT_BILLING_CYCLE_DAY_LOCAL : currentAccount.getBillCycleDayLocal());
         }
 
         // Set all updatable fields with the new values if non null, otherwise defaults to the current values
-        accountData.setEmail(Objects.firstNonNull(email, currentAccount.getEmail()));
-        accountData.setName(Objects.firstNonNull(name, currentAccount.getName()));
-        accountData.setFirstNameLength(Objects.firstNonNull(firstNameLength, currentAccount.getFirstNameLength()));
-        accountData.setPaymentMethodId(Optional.<UUID>fromNullable(paymentMethodId)
-                                               .or(Optional.<UUID>fromNullable(currentAccount.getPaymentMethodId())).orNull());
-        accountData.setTimeZone(Objects.firstNonNull(timeZone, currentAccount.getTimeZone()));
-        accountData.setLocale(Objects.firstNonNull(locale, currentAccount.getLocale()));
-        accountData.setAddress1(Objects.firstNonNull(address1, currentAccount.getAddress1()));
-        accountData.setAddress2(Objects.firstNonNull(address2, currentAccount.getAddress2()));
-        accountData.setCompanyName(Objects.firstNonNull(companyName, currentAccount.getCompanyName()));
-        accountData.setCity(Objects.firstNonNull(city, currentAccount.getCity()));
-        accountData.setStateOrProvince(Objects.firstNonNull(stateOrProvince, currentAccount.getStateOrProvince()));
-        accountData.setCountry(Objects.firstNonNull(country, currentAccount.getCountry()));
-        accountData.setPostalCode(Objects.firstNonNull(postalCode, currentAccount.getPostalCode()));
-        accountData.setPhone(Objects.firstNonNull(phone, currentAccount.getPhone()));
-        accountData.setIsMigrated(Objects.firstNonNull(isMigrated, currentAccount.isMigrated()));
-        accountData.setIsNotifiedForInvoices(Objects.firstNonNull(isNotifiedForInvoices, currentAccount.isNotifiedForInvoices()));
+        accountData.setEmail(email != null ? email : currentAccount.getEmail());
+        accountData.setName(name != null ? name : currentAccount.getName());
+        final Integer firstNameLength = this.firstNameLength != null ? this.firstNameLength : currentAccount.getFirstNameLength();
+        if (firstNameLength != null) {
+            accountData.setFirstNameLength(firstNameLength);
+        }
+        accountData.setPaymentMethodId(paymentMethodId != null ? paymentMethodId : currentAccount.getPaymentMethodId());
+        accountData.setTimeZone(timeZone != null ? timeZone : currentAccount.getTimeZone());
+        accountData.setLocale(locale != null ? locale : currentAccount.getLocale());
+        accountData.setAddress1(address1 != null ? address1 : currentAccount.getAddress1());
+        accountData.setAddress2(address2 != null ? address2 : currentAccount.getAddress2());
+        accountData.setCompanyName(companyName != null ? companyName : currentAccount.getCompanyName());
+        accountData.setCity(city != null ? city : currentAccount.getCity());
+        accountData.setStateOrProvince(stateOrProvince != null ? stateOrProvince : currentAccount.getStateOrProvince());
+        accountData.setCountry(country != null ? country : currentAccount.getCountry());
+        accountData.setPostalCode(postalCode != null ? postalCode : currentAccount.getPostalCode());
+        accountData.setPhone(phone != null ? phone : currentAccount.getPhone());
+        final Boolean isMigrated = this.isMigrated != null ? this.isMigrated : currentAccount.isMigrated();
+        if (isMigrated != null) {
+            accountData.setIsMigrated(isMigrated);
+        }
+        final Boolean isNotifiedForInvoices = this.isNotifiedForInvoices != null ? this.isNotifiedForInvoices : currentAccount.isNotifiedForInvoices();
+        if (isNotifiedForInvoices != null) {
+            accountData.setIsNotifiedForInvoices(isNotifiedForInvoices);
+        }
 
         return new DefaultAccount(currentAccount.getId(), accountData);
     }
@@ -413,5 +457,4 @@ public class DefaultAccount extends EntityBase implements Account {
         result = 31 * result + (isNotifiedForInvoices != null ? isNotifiedForInvoices.hashCode() : 0);
         return result;
     }
-
 }
diff --git a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountCreationEvent.java b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountCreationEvent.java
index f0fa854..afd5664 100644
--- a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountCreationEvent.java
+++ b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountCreationEvent.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -19,7 +21,6 @@ package org.killbill.billing.account.api.user;
 import java.util.UUID;
 
 import org.joda.time.DateTimeZone;
-
 import org.killbill.billing.account.api.AccountData;
 import org.killbill.billing.account.dao.AccountModelDao;
 import org.killbill.billing.catalog.api.Currency;
@@ -29,6 +30,7 @@ import org.killbill.billing.events.BusEventBase;
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Strings;
 
 public class DefaultAccountCreationEvent extends BusEventBase implements AccountCreationInternalEvent {
 
@@ -46,7 +48,6 @@ public class DefaultAccountCreationEvent extends BusEventBase implements Account
         this.data = data;
     }
 
-
     @JsonIgnore
     @Override
     public BusInternalEventType getBusEventType() {
@@ -124,7 +125,7 @@ public class DefaultAccountCreationEvent extends BusEventBase implements Account
         private final Boolean isNotifiedForInvoices;
 
         public DefaultAccountData(final AccountModelDao d) {
-            this(d.getExternalKey() != null ? d.getExternalKey() : null,
+            this(d.getExternalKey(),
                  d.getName(),
                  d.getFirstNameLength(),
                  d.getEmail(),
@@ -213,13 +214,21 @@ public class DefaultAccountCreationEvent extends BusEventBase implements Account
 
         @Override
         public Currency getCurrency() {
-            return currency == null ? null : Currency.valueOf(currency);
+            if (Strings.emptyToNull(currency) == null) {
+                return null;
+            } else {
+                return Currency.valueOf(currency);
+            }
         }
 
         @JsonIgnore
         @Override
         public DateTimeZone getTimeZone() {
-            return DateTimeZone.forID(timeZone);
+            if (Strings.emptyToNull(timeZone) == null) {
+                return null;
+            } else {
+                return DateTimeZone.forID(timeZone);
+            }
         }
 
         @JsonProperty("timeZone")
diff --git a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java
index 283bec0..55fb71b 100644
--- a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java
+++ b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -31,7 +33,6 @@ import org.killbill.billing.account.dao.AccountDao;
 import org.killbill.billing.account.dao.AccountEmailModelDao;
 import org.killbill.billing.account.dao.AccountModelDao;
 import org.killbill.billing.util.callcontext.CallContext;
-import org.killbill.billing.util.callcontext.CallContextFactory;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.entity.Pagination;
@@ -46,14 +47,11 @@ import static org.killbill.billing.util.entity.dao.DefaultPaginationHelper.getEn
 
 public class DefaultAccountUserApi implements AccountUserApi {
 
-    private final CallContextFactory callContextFactory;
     private final InternalCallContextFactory internalCallContextFactory;
     private final AccountDao accountDao;
 
     @Inject
-    public DefaultAccountUserApi(final CallContextFactory callContextFactory, final InternalCallContextFactory internalCallContextFactory,
-                                 final AccountDao accountDao) {
-        this.callContextFactory = callContextFactory;
+    public DefaultAccountUserApi(final InternalCallContextFactory internalCallContextFactory, final AccountDao accountDao) {
         this.internalCallContextFactory = internalCallContextFactory;
         this.accountDao = accountDao;
     }
@@ -158,11 +156,14 @@ public class DefaultAccountUserApi implements AccountUserApi {
     }
 
     private void updateAccount(final Account currentAccount, final AccountData accountData, final CallContext context) throws AccountApiException {
-        // Set unspecified (null) fields to their current values
         final Account updatedAccount = new DefaultAccount(currentAccount.getId(), accountData);
-        final AccountModelDao accountToUpdate = new AccountModelDao(currentAccount.getId(), updatedAccount.mergeWithDelegate(currentAccount));
 
-        accountDao.update(accountToUpdate, internalCallContextFactory.createInternalCallContext(accountToUpdate.getId(), context));
+        // Set unspecified (null) fields to their current values
+        final Account mergedAccount = updatedAccount.mergeWithDelegate(currentAccount);
+
+        final AccountModelDao updatedAccountModelDao = new AccountModelDao(currentAccount.getId(), mergedAccount);
+
+        accountDao.update(updatedAccountModelDao, internalCallContextFactory.createInternalCallContext(updatedAccountModelDao.getId(), context));
     }
 
     @Override
diff --git a/account/src/main/java/org/killbill/billing/account/dao/AccountModelDao.java b/account/src/main/java/org/killbill/billing/account/dao/AccountModelDao.java
index 44a9deb..353812e 100644
--- a/account/src/main/java/org/killbill/billing/account/dao/AccountModelDao.java
+++ b/account/src/main/java/org/killbill/billing/account/dao/AccountModelDao.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -30,7 +32,9 @@ import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
 import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
+
+import static org.killbill.billing.account.api.DefaultMutableAccountData.DEFAULT_BILLING_CYCLE_DAY_LOCAL;
 
 public class AccountModelDao extends EntityModelDaoBase implements EntityModelDao<Account> {
 
@@ -63,7 +67,7 @@ public class AccountModelDao extends EntityModelDaoBase implements EntityModelDa
                            final String city, final String stateOrProvince, final String country, final String postalCode,
                            final String phone, final Boolean migrated, final Boolean notifiedForInvoices) {
         super(id, createdDate, updatedDate);
-        this.externalKey = Objects.firstNonNull(externalKey, id.toString());
+        this.externalKey = MoreObjects.firstNonNull(externalKey, id.toString());
         this.email = email;
         this.name = name;
         this.firstNameLength = firstNameLength;
@@ -85,11 +89,29 @@ public class AccountModelDao extends EntityModelDaoBase implements EntityModelDa
     }
 
     public AccountModelDao(final UUID id, @Nullable final DateTime createdDate, final DateTime updatedDate, final AccountData account) {
-        this(id, createdDate, updatedDate, account.getExternalKey(),
-             account.getEmail(), account.getName(), account.getFirstNameLength(), account.getCurrency(),
-             account.getBillCycleDayLocal() == null ? 0 : account.getBillCycleDayLocal(), account.getPaymentMethodId(), account.getTimeZone(), account.getLocale(), account.getAddress1(), account.getAddress2(),
-             account.getCompanyName(), account.getCity(), account.getStateOrProvince(), account.getCountry(), account.getPostalCode(),
-             account.getPhone(), account.isMigrated(), account.isNotifiedForInvoices());
+        this(id,
+             createdDate,
+             updatedDate,
+             account.getExternalKey(),
+             account.getEmail(),
+             account.getName(),
+             account.getFirstNameLength(),
+             account.getCurrency(),
+             MoreObjects.firstNonNull(account.getBillCycleDayLocal(), DEFAULT_BILLING_CYCLE_DAY_LOCAL),
+             account.getPaymentMethodId(),
+             account.getTimeZone(),
+             account.getLocale(),
+             account.getAddress1(),
+             account.getAddress2(),
+             account.getCompanyName(),
+             account.getCity(),
+             account.getStateOrProvince(),
+             account.getCountry(),
+             account.getPostalCode(),
+             account.getPhone(),
+             account.isMigrated(),
+             // There is a NOT NULL constraint on the is_notified_for_invoices column
+             MoreObjects.firstNonNull(account.isNotifiedForInvoices(), false));
     }
 
     public AccountModelDao(final UUID id, final AccountData account) {
@@ -104,152 +126,152 @@ public class AccountModelDao extends EntityModelDaoBase implements EntityModelDa
         return externalKey;
     }
 
+    public void setExternalKey(final String externalKey) {
+        this.externalKey = externalKey;
+    }
+
     public String getEmail() {
         return email;
     }
 
+    public void setEmail(final String email) {
+        this.email = email;
+    }
+
     public String getName() {
         return name;
     }
 
+    public void setName(final String name) {
+        this.name = name;
+    }
+
     public Integer getFirstNameLength() {
         return firstNameLength;
     }
 
+    public void setFirstNameLength(final Integer firstNameLength) {
+        this.firstNameLength = firstNameLength;
+    }
+
     public Currency getCurrency() {
         return currency;
     }
 
+    public void setCurrency(final Currency currency) {
+        this.currency = currency;
+    }
+
     public Integer getBillingCycleDayLocal() {
         return billingCycleDayLocal;
     }
 
+    public void setBillingCycleDayLocal(final Integer billingCycleDayLocal) {
+        this.billingCycleDayLocal = MoreObjects.firstNonNull(billingCycleDayLocal, DEFAULT_BILLING_CYCLE_DAY_LOCAL);
+    }
+
     public UUID getPaymentMethodId() {
         return paymentMethodId;
     }
 
+    public void setPaymentMethodId(final UUID paymentMethodId) {
+        this.paymentMethodId = paymentMethodId;
+    }
+
     public DateTimeZone getTimeZone() {
         return timeZone;
     }
 
+    public void setTimeZone(final DateTimeZone timeZone) {
+        this.timeZone = timeZone;
+    }
+
     public String getLocale() {
         return locale;
     }
 
+    public void setLocale(final String locale) {
+        this.locale = locale;
+    }
+
     public String getAddress1() {
         return address1;
     }
 
+    public void setAddress1(final String address1) {
+        this.address1 = address1;
+    }
+
     public String getAddress2() {
         return address2;
     }
 
+    public void setAddress2(final String address2) {
+        this.address2 = address2;
+    }
+
     public String getCompanyName() {
         return companyName;
     }
 
+    public void setCompanyName(final String companyName) {
+        this.companyName = companyName;
+    }
+
     public String getCity() {
         return city;
     }
 
+    public void setCity(final String city) {
+        this.city = city;
+    }
+
     public String getStateOrProvince() {
         return stateOrProvince;
     }
 
+    public void setStateOrProvince(final String stateOrProvince) {
+        this.stateOrProvince = stateOrProvince;
+    }
+
     public String getCountry() {
         return country;
     }
 
+    public void setCountry(final String country) {
+        this.country = country;
+    }
+
     public String getPostalCode() {
         return postalCode;
     }
 
+    public void setPostalCode(final String postalCode) {
+        this.postalCode = postalCode;
+    }
+
     public String getPhone() {
         return phone;
     }
 
+    public void setPhone(final String phone) {
+        this.phone = phone;
+    }
+
     public Boolean getMigrated() {
         return migrated;
     }
 
+    public void setMigrated(final Boolean migrated) {
+        this.migrated = migrated;
+    }
+
     // TODO Required for making the BindBeanFactory with Introspector work
     // see Introspector line 571; they look at public method.
     public Boolean getIsNotifiedForInvoices() {
         return isNotifiedForInvoices;
     }
 
-    public void setExternalKey(final String externalKey) {
-        this.externalKey = externalKey;
-    }
-
-    public void setEmail(final String email) {
-        this.email = email;
-    }
-
-    public void setName(final String name) {
-        this.name = name;
-    }
-
-    public void setFirstNameLength(final Integer firstNameLength) {
-        this.firstNameLength = firstNameLength;
-    }
-
-    public void setCurrency(final Currency currency) {
-        this.currency = currency;
-    }
-
-    public void setBillingCycleDayLocal(final int billingCycleDayLocal) {
-        this.billingCycleDayLocal = billingCycleDayLocal;
-    }
-
-    public void setPaymentMethodId(final UUID paymentMethodId) {
-        this.paymentMethodId = paymentMethodId;
-    }
-
-    public void setTimeZone(final DateTimeZone timeZone) {
-        this.timeZone = timeZone;
-    }
-
-    public void setLocale(final String locale) {
-        this.locale = locale;
-    }
-
-    public void setAddress1(final String address1) {
-        this.address1 = address1;
-    }
-
-    public void setAddress2(final String address2) {
-        this.address2 = address2;
-    }
-
-    public void setCompanyName(final String companyName) {
-        this.companyName = companyName;
-    }
-
-    public void setCity(final String city) {
-        this.city = city;
-    }
-
-    public void setStateOrProvince(final String stateOrProvince) {
-        this.stateOrProvince = stateOrProvince;
-    }
-
-    public void setCountry(final String country) {
-        this.country = country;
-    }
-
-    public void setPostalCode(final String postalCode) {
-        this.postalCode = postalCode;
-    }
-
-    public void setPhone(final String phone) {
-        this.phone = phone;
-    }
-
-    public void setMigrated(final Boolean migrated) {
-        this.migrated = migrated;
-    }
-
     public void setIsNotifiedForInvoices(final Boolean isNotifiedForInvoices) {
         this.isNotifiedForInvoices = isNotifiedForInvoices;
     }
diff --git a/account/src/main/resources/org/killbill/billing/account/ddl.sql b/account/src/main/resources/org/killbill/billing/account/ddl.sql
index 6e43399..014d23c 100644
--- a/account/src/main/resources/org/killbill/billing/account/ddl.sql
+++ b/account/src/main/resources/org/killbill/billing/account/ddl.sql
@@ -5,9 +5,9 @@ CREATE TABLE accounts (
     record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
     id char(36) NOT NULL,
     external_key varchar(128) NULL,
-    email varchar(128) NOT NULL,
-    name varchar(100) NOT NULL,
-    first_name_length int NOT NULL,
+    email varchar(128) DEFAULT NULL,
+    name varchar(100) DEFAULT NULL,
+    first_name_length int DEFAULT NULL,
     currency char(3) DEFAULT NULL,
     billing_cycle_day_local int DEFAULT NULL,
     payment_method_id char(36) DEFAULT NULL,
@@ -40,9 +40,9 @@ CREATE TABLE account_history (
     id char(36) NOT NULL,
     target_record_id int(11) unsigned NOT NULL,
     external_key varchar(128) NULL,
-    email varchar(128) NOT NULL,
-    name varchar(100) NOT NULL,
-    first_name_length int NOT NULL,
+    email varchar(128) DEFAULT NULL,
+    name varchar(100) DEFAULT NULL,
+    first_name_length int DEFAULT NULL,
     currency char(3) DEFAULT NULL,
     billing_cycle_day_local int DEFAULT NULL,
     payment_method_id char(36) DEFAULT NULL,
diff --git a/account/src/test/java/org/killbill/billing/account/api/TestDefaultAccount.java b/account/src/test/java/org/killbill/billing/account/api/TestDefaultAccount.java
index 18e6857..1d8c654 100644
--- a/account/src/test/java/org/killbill/billing/account/api/TestDefaultAccount.java
+++ b/account/src/test/java/org/killbill/billing/account/api/TestDefaultAccount.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -19,56 +21,85 @@ package org.killbill.billing.account.api;
 import java.util.UUID;
 
 import org.joda.time.DateTimeZone;
+import org.killbill.billing.account.AccountTestSuiteNoDB;
+import org.killbill.billing.catalog.api.Currency;
 import org.mockito.Mockito;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
-import org.killbill.billing.account.AccountTestSuiteNoDB;
-import org.killbill.billing.catalog.api.Currency;
-
 public class TestDefaultAccount extends AccountTestSuiteNoDB {
 
-    @Test(groups = "fast", description="Test if Account constructor can accept null values")
+    @Test(groups = "fast", description = "Test if Account constructor can accept null values")
     public void testConstructorAcceptsNullValues() throws Exception {
         final AccountData accountData = getNullAccountData();
         final Account account = new DefaultAccount(UUID.randomUUID(), accountData);
 
-        Assert.assertEquals(account.getExternalKey(), DefaultAccount.DEFAULT_STRING_VALUE);
-        Assert.assertEquals(account.getEmail(), DefaultAccount.DEFAULT_STRING_VALUE);
-        Assert.assertEquals(account.getName(), DefaultAccount.DEFAULT_STRING_VALUE);
-        Assert.assertEquals(account.getFirstNameLength(), DefaultAccount.DEFAULT_INTEGER_VALUE);
-        Assert.assertEquals(account.getCurrency(), DefaultAccount.DEFAULT_CURRENCY_VALUE);
-        Assert.assertEquals(account.getBillCycleDayLocal(), DefaultAccount.DEFAULT_INTEGER_VALUE);
+        Assert.assertNull(account.getExternalKey());
+        Assert.assertNull(account.getEmail());
+        Assert.assertNull(account.getName());
+        Assert.assertNull(account.getFirstNameLength());
+        Assert.assertNull(account.getCurrency());
+        Assert.assertEquals(account.getBillCycleDayLocal(), (Integer) 0);
         Assert.assertNull(account.getPaymentMethodId());
-        Assert.assertEquals(account.getTimeZone(), DefaultAccount.DEFAULT_TIMEZONE_VALUE);
-        Assert.assertEquals(account.getLocale(), DefaultAccount.DEFAULT_STRING_VALUE);
-        Assert.assertEquals(account.getAddress1(), DefaultAccount.DEFAULT_STRING_VALUE);
-        Assert.assertEquals(account.getAddress2(), DefaultAccount.DEFAULT_STRING_VALUE);
-        Assert.assertEquals(account.getCompanyName(), DefaultAccount.DEFAULT_STRING_VALUE);
-        Assert.assertEquals(account.getCity(), DefaultAccount.DEFAULT_STRING_VALUE);
-        Assert.assertEquals(account.getStateOrProvince(), DefaultAccount.DEFAULT_STRING_VALUE);
-        Assert.assertEquals(account.getCountry(), DefaultAccount.DEFAULT_STRING_VALUE);
-        Assert.assertEquals(account.getPostalCode(), DefaultAccount.DEFAULT_STRING_VALUE);
-        Assert.assertEquals(account.getPhone(), DefaultAccount.DEFAULT_STRING_VALUE);
-        Assert.assertEquals(account.isMigrated(), DefaultAccount.DEFAULT_MIGRATED_VALUE);
-        Assert.assertEquals(account.isNotifiedForInvoices(), DefaultAccount.DEFAULT_NOTIFIED_FOR_INVOICES_VALUE);
+        Assert.assertNull(account.getTimeZone());
+        Assert.assertNull(account.getLocale());
+        Assert.assertNull(account.getAddress1());
+        Assert.assertNull(account.getAddress2());
+        Assert.assertNull(account.getCompanyName());
+        Assert.assertNull(account.getCity());
+        Assert.assertNull(account.getStateOrProvince());
+        Assert.assertNull(account.getCountry());
+        Assert.assertNull(account.getPostalCode());
+        Assert.assertNull(account.getPhone());
+        Assert.assertNull(account.isMigrated());
+        Assert.assertNull(account.isNotifiedForInvoices());
     }
 
-    @Test(groups = "fast", description="Test mergeWithDelegate Account api")
+    @Test(groups = "fast", description = "Test mergeWithDelegate Account api")
     public void testMergeWithDelegate() throws Exception {
         final AccountData accountData = getNullAccountData();
         final Account account = new DefaultAccount(UUID.randomUUID(), accountData);
 
-        final AccountData secondAccountData = getAccountData();
-        final Account secondAccount = new DefaultAccount(UUID.randomUUID(), secondAccountData);
-
-        final Account finalAccount = account.mergeWithDelegate(secondAccount);
-        checkAccountEquals(finalAccount, secondAccount);
+        // Update all updatable fields
+        final AccountData accountDataUpdates1 = getAccountData(account.getBillCycleDayLocal(), account.getCurrency(), account.getExternalKey());
+        final Account accountUpdates1 = new DefaultAccount(UUID.randomUUID(), accountDataUpdates1);
+
+        final Account updatedAccount1 = accountUpdates1.mergeWithDelegate(account);
+        checkAccountEquals(updatedAccount1, accountUpdates1);
+
+        // Update some fields
+        final AccountData accountDataUpdates2 = Mockito.mock(AccountData.class);
+        Mockito.when(accountDataUpdates2.getEmail()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(accountDataUpdates2.getName()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(accountDataUpdates2.getFirstNameLength()).thenReturn(12);
+        Mockito.when(accountDataUpdates2.isNotifiedForInvoices()).thenReturn(true);
+        final Account accountUpdates2 = new DefaultAccount(UUID.randomUUID(), accountDataUpdates2);
+
+        final Account updatedAccount2 = accountUpdates2.mergeWithDelegate(updatedAccount1);
+        Assert.assertEquals(updatedAccount2.getEmail(), accountUpdates2.getEmail());
+        Assert.assertEquals(updatedAccount2.getName(), accountUpdates2.getName());
+        Assert.assertEquals(updatedAccount2.getFirstNameLength(), updatedAccount2.getFirstNameLength());
+        Assert.assertEquals(updatedAccount2.isNotifiedForInvoices(), updatedAccount2.isNotifiedForInvoices());
+        Assert.assertEquals(updatedAccount2.getExternalKey(), updatedAccount1.getExternalKey());
+        Assert.assertEquals(updatedAccount2.getCurrency(), updatedAccount1.getCurrency());
+        Assert.assertEquals(updatedAccount2.getBillCycleDayLocal(), updatedAccount1.getBillCycleDayLocal());
+        Assert.assertEquals(updatedAccount2.getPaymentMethodId(), updatedAccount1.getPaymentMethodId());
+        Assert.assertEquals(updatedAccount2.getTimeZone(), updatedAccount1.getTimeZone());
+        Assert.assertEquals(updatedAccount2.getLocale(), updatedAccount1.getLocale());
+        Assert.assertEquals(updatedAccount2.getAddress1(), updatedAccount1.getAddress1());
+        Assert.assertEquals(updatedAccount2.getAddress2(), updatedAccount1.getAddress2());
+        Assert.assertEquals(updatedAccount2.getCompanyName(), updatedAccount1.getCompanyName());
+        Assert.assertEquals(updatedAccount2.getCity(), updatedAccount1.getCity());
+        Assert.assertEquals(updatedAccount2.getStateOrProvince(), updatedAccount1.getStateOrProvince());
+        Assert.assertEquals(updatedAccount2.getCountry(), updatedAccount1.getCountry());
+        Assert.assertEquals(updatedAccount2.getPostalCode(), updatedAccount1.getPostalCode());
+        Assert.assertEquals(updatedAccount2.getPhone(), updatedAccount1.getPhone());
+        Assert.assertEquals(updatedAccount2.isMigrated(), updatedAccount1.isMigrated());
     }
 
-    @Test(groups = "fast", description="Test Account BCD merge")
+    @Test(groups = "fast", description = "Test Account BCD merge")
     public void testBCDMerges() throws Exception {
         final UUID accountId = UUID.randomUUID();
         final Currency currency = Currency.BRL;
@@ -97,7 +128,7 @@ public class TestDefaultAccount extends AccountTestSuiteNoDB {
             // Different BCD
             Assert.assertEquals(accountWithAnotherBCD.mergeWithDelegate(accountWithRealBCD).getBillCycleDayLocal(), (Integer) 20);
             Assert.fail();
-        } catch (IllegalArgumentException e) {
+        } catch (final IllegalArgumentException e) {
             Assert.assertTrue(true);
         }
     }
@@ -124,10 +155,6 @@ public class TestDefaultAccount extends AccountTestSuiteNoDB {
         Assert.assertEquals(finalAccount.isNotifiedForInvoices(), delegateAccount.isNotifiedForInvoices());
     }
 
-    private AccountData getAccountData() {
-        return getAccountData(Integer.MIN_VALUE, Currency.AUD, UUID.randomUUID().toString());
-    }
-
     private AccountData getAccountData(final Integer bcd, final Currency currency, final String externalKey) {
         final AccountData secondAccountData = Mockito.mock(AccountData.class);
         Mockito.when(secondAccountData.getExternalKey()).thenReturn(externalKey);
@@ -147,7 +174,7 @@ public class TestDefaultAccount extends AccountTestSuiteNoDB {
         Mockito.when(secondAccountData.getCountry()).thenReturn(UUID.randomUUID().toString());
         Mockito.when(secondAccountData.getPostalCode()).thenReturn(UUID.randomUUID().toString());
         Mockito.when(secondAccountData.getPhone()).thenReturn(UUID.randomUUID().toString());
-        Mockito.when(secondAccountData.isMigrated()).thenReturn(true);
+        Mockito.when(secondAccountData.isMigrated()).thenReturn(false);
         Mockito.when(secondAccountData.isNotifiedForInvoices()).thenReturn(true);
         return secondAccountData;
     }
diff --git a/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java b/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java
index 07a9682..1843b08 100644
--- a/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java
+++ b/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -19,11 +21,6 @@ package org.killbill.billing.account.api.user;
 import java.util.UUID;
 
 import org.joda.time.DateTimeZone;
-import org.mockito.Mockito;
-import org.testng.Assert;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
 import org.killbill.billing.account.AccountTestSuiteNoDB;
 import org.killbill.billing.account.api.AccountData;
 import org.killbill.billing.account.api.AccountEmail;
@@ -32,12 +29,16 @@ import org.killbill.billing.account.api.DefaultAccountEmail;
 import org.killbill.billing.account.dao.AccountDao;
 import org.killbill.billing.account.dao.AccountModelDao;
 import org.killbill.billing.account.dao.MockAccountDao;
-import org.killbill.bus.api.PersistentBus;
+import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.CallContextFactory;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.bus.api.PersistentBus;
+import org.mockito.Mockito;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
 
 public class TestDefaultAccountUserApiWithMocks extends AccountTestSuiteNoDB {
 
@@ -52,10 +53,10 @@ public class TestDefaultAccountUserApiWithMocks extends AccountTestSuiteNoDB {
     @BeforeMethod(groups = "fast")
     public void setUp() throws Exception {
         accountDao = new MockAccountDao(Mockito.mock(PersistentBus.class));
-        accountUserApi = new DefaultAccountUserApi(factory, internalFactory, accountDao);
+        accountUserApi = new DefaultAccountUserApi(internalFactory, accountDao);
     }
 
-    @Test(groups = "fast", description="Test Account create API")
+    @Test(groups = "fast", description = "Test Account create API")
     public void testCreateAccount() throws Exception {
         final UUID id = UUID.randomUUID();
         final String externalKey = UUID.randomUUID().toString();
@@ -105,7 +106,7 @@ public class TestDefaultAccountUserApiWithMocks extends AccountTestSuiteNoDB {
         Assert.assertEquals(account.getIsNotifiedForInvoices(), isNotifiedForInvoices);
     }
 
-    @Test(groups = "fast", description="Test ability to add email to Account")
+    @Test(groups = "fast", description = "Test ability to add email to Account")
     public void testAddEmail() throws Exception {
         final UUID accountId = UUID.randomUUID();
 
diff --git a/api/src/main/java/org/killbill/billing/account/api/DefaultMutableAccountData.java b/api/src/main/java/org/killbill/billing/account/api/DefaultMutableAccountData.java
index c98705f..010a76d 100644
--- a/api/src/main/java/org/killbill/billing/account/api/DefaultMutableAccountData.java
+++ b/api/src/main/java/org/killbill/billing/account/api/DefaultMutableAccountData.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -19,15 +21,17 @@ package org.killbill.billing.account.api;
 import java.util.UUID;
 
 import org.joda.time.DateTimeZone;
-
 import org.killbill.billing.catalog.api.Currency;
 
 public class DefaultMutableAccountData implements MutableAccountData {
 
+    // 0 has a special meaning in Junction
+    public static final int DEFAULT_BILLING_CYCLE_DAY_LOCAL = 0;
+
     private String externalKey;
     private String email;
     private String name;
-    private int firstNameLength;
+    private Integer firstNameLength;
     private Currency currency;
     private int billCycleDayLocal;
     private UUID paymentMethodId;
@@ -41,8 +45,8 @@ public class DefaultMutableAccountData implements MutableAccountData {
     private String country;
     private String postalCode;
     private String phone;
-    private boolean isMigrated;
-    private boolean isNotifiedForInvoices;
+    private Boolean isMigrated;
+    private Boolean isNotifiedForInvoices;
 
     public DefaultMutableAccountData(final String externalKey, final String email, final String name,
                                      final int firstNameLength, final Currency currency, final int billCycleDayLocal,
@@ -78,7 +82,7 @@ public class DefaultMutableAccountData implements MutableAccountData {
         this.name = accountData.getName();
         this.firstNameLength = accountData.getFirstNameLength();
         this.currency = accountData.getCurrency();
-        this.billCycleDayLocal = accountData.getBillCycleDayLocal();
+        this.billCycleDayLocal = accountData.getBillCycleDayLocal() == null ? DEFAULT_BILLING_CYCLE_DAY_LOCAL : accountData.getBillCycleDayLocal();
         this.paymentMethodId = accountData.getPaymentMethodId();
         this.timeZone = accountData.getTimeZone();
         this.locale = accountData.getLocale();
@@ -100,178 +104,178 @@ public class DefaultMutableAccountData implements MutableAccountData {
     }
 
     @Override
-    public String getEmail() {
-        return email;
+    public void setExternalKey(final String externalKey) {
+        this.externalKey = externalKey;
     }
 
     @Override
-    public String getName() {
-        return name;
+    public String getEmail() {
+        return email;
     }
 
     @Override
-    public Integer getFirstNameLength() {
-        return firstNameLength;
+    public void setEmail(final String email) {
+        this.email = email;
     }
 
     @Override
-    public Currency getCurrency() {
-        return currency;
+    public String getName() {
+        return name;
     }
 
     @Override
-    public Integer getBillCycleDayLocal() {
-        return billCycleDayLocal;
+    public void setName(final String name) {
+        this.name = name;
     }
 
     @Override
-    public UUID getPaymentMethodId() {
-        return paymentMethodId;
+    public Integer getFirstNameLength() {
+        return firstNameLength;
     }
 
     @Override
-    public DateTimeZone getTimeZone() {
-        return timeZone;
+    public void setFirstNameLength(final int firstNameLength) {
+        this.firstNameLength = firstNameLength;
     }
 
     @Override
-    public String getLocale() {
-        return locale;
+    public Currency getCurrency() {
+        return currency;
     }
 
     @Override
-    public String getAddress1() {
-        return address1;
+    public void setCurrency(final Currency currency) {
+        this.currency = currency;
     }
 
     @Override
-    public String getAddress2() {
-        return address2;
+    public Integer getBillCycleDayLocal() {
+        return billCycleDayLocal;
     }
 
     @Override
-    public String getCompanyName() {
-        return companyName;
+    public void setBillCycleDayLocal(final int billCycleDayLocal) {
+        this.billCycleDayLocal = billCycleDayLocal;
     }
 
     @Override
-    public String getCity() {
-        return city;
+    public UUID getPaymentMethodId() {
+        return paymentMethodId;
     }
 
     @Override
-    public String getStateOrProvince() {
-        return stateOrProvince;
+    public void setPaymentMethodId(final UUID paymentMethodId) {
+        this.paymentMethodId = paymentMethodId;
     }
 
     @Override
-    public String getCountry() {
-        return country;
+    public DateTimeZone getTimeZone() {
+        return timeZone;
     }
 
     @Override
-    public String getPostalCode() {
-        return postalCode;
+    public void setTimeZone(final DateTimeZone timeZone) {
+        this.timeZone = timeZone;
     }
 
     @Override
-    public String getPhone() {
-        return phone;
+    public String getLocale() {
+        return locale;
     }
 
     @Override
-    public Boolean isMigrated() {
-        return isMigrated;
+    public void setLocale(final String locale) {
+        this.locale = locale;
     }
 
     @Override
-    public Boolean isNotifiedForInvoices() {
-        return isNotifiedForInvoices;
+    public String getAddress1() {
+        return address1;
     }
 
     @Override
-    public void setExternalKey(final String externalKey) {
-        this.externalKey = externalKey;
+    public void setAddress1(final String address1) {
+        this.address1 = address1;
     }
 
     @Override
-    public void setEmail(final String email) {
-        this.email = email;
+    public String getAddress2() {
+        return address2;
     }
 
     @Override
-    public void setName(final String name) {
-        this.name = name;
+    public void setAddress2(final String address2) {
+        this.address2 = address2;
     }
 
     @Override
-    public void setFirstNameLength(final int firstNameLength) {
-        this.firstNameLength = firstNameLength;
+    public String getCompanyName() {
+        return companyName;
     }
 
     @Override
-    public void setCurrency(final Currency currency) {
-        this.currency = currency;
+    public void setCompanyName(final String companyName) {
+        this.companyName = companyName;
     }
 
     @Override
-    public void setBillCycleDayLocal(final int billCycleDayLocal) {
-        this.billCycleDayLocal = billCycleDayLocal;
+    public String getCity() {
+        return city;
     }
 
     @Override
-    public void setPaymentMethodId(final UUID paymentMethodId) {
-        this.paymentMethodId = paymentMethodId;
+    public void setCity(final String city) {
+        this.city = city;
     }
 
     @Override
-    public void setTimeZone(final DateTimeZone timeZone) {
-        this.timeZone = timeZone;
+    public String getStateOrProvince() {
+        return stateOrProvince;
     }
 
     @Override
-    public void setLocale(final String locale) {
-        this.locale = locale;
+    public void setStateOrProvince(final String stateOrProvince) {
+        this.stateOrProvince = stateOrProvince;
     }
 
     @Override
-    public void setAddress1(final String address1) {
-        this.address1 = address1;
+    public String getCountry() {
+        return country;
     }
 
     @Override
-    public void setAddress2(final String address2) {
-        this.address2 = address2;
+    public void setCountry(final String country) {
+        this.country = country;
     }
 
     @Override
-    public void setCompanyName(final String companyName) {
-        this.companyName = companyName;
+    public String getPostalCode() {
+        return postalCode;
     }
 
     @Override
-    public void setCity(final String city) {
-        this.city = city;
+    public void setPostalCode(final String postalCode) {
+        this.postalCode = postalCode;
     }
 
     @Override
-    public void setStateOrProvince(final String stateOrProvince) {
-        this.stateOrProvince = stateOrProvince;
+    public String getPhone() {
+        return phone;
     }
 
     @Override
-    public void setCountry(final String country) {
-        this.country = country;
+    public void setPhone(final String phone) {
+        this.phone = phone;
     }
 
     @Override
-    public void setPostalCode(final String postalCode) {
-        this.postalCode = postalCode;
+    public Boolean isMigrated() {
+        return isMigrated;
     }
 
     @Override
-    public void setPhone(final String phone) {
-        this.phone = phone;
+    public Boolean isNotifiedForInvoices() {
+        return isNotifiedForInvoices;
     }
 
     @Override
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/notification/EmailInvoiceNotifier.java b/invoice/src/main/java/org/killbill/billing/invoice/notification/EmailInvoiceNotifier.java
index b96220b..b0525bd 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/notification/EmailInvoiceNotifier.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/notification/EmailInvoiceNotifier.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -24,23 +26,24 @@ import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountEmail;
+import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceNotifier;
 import org.killbill.billing.invoice.template.HtmlInvoice;
 import org.killbill.billing.invoice.template.HtmlInvoiceGenerator;
+import org.killbill.billing.tag.TagInternalApi;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.email.DefaultEmailSender;
 import org.killbill.billing.util.email.EmailApiException;
 import org.killbill.billing.util.email.EmailConfig;
 import org.killbill.billing.util.email.EmailSender;
-import org.killbill.billing.account.api.AccountInternalApi;
-import org.killbill.billing.tag.TagInternalApi;
 import org.killbill.billing.util.tag.ControlTagType;
 import org.killbill.billing.util.tag.Tag;
 
+import com.google.common.base.Strings;
 import com.google.inject.Inject;
 
 public class EmailInvoiceNotifier implements InvoiceNotifier {
@@ -66,6 +69,10 @@ public class EmailInvoiceNotifier implements InvoiceNotifier {
 
     @Override
     public void notify(final Account account, final Invoice invoice, final TenantContext context) throws InvoiceApiException {
+        if (Strings.emptyToNull(account.getEmail()) == null) {
+            throw new InvoiceApiException(new IllegalArgumentException("Email for account " + account.getId() + " not specified"), ErrorCode.EMAIL_SENDING_FAILED);
+        }
+
         final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(account.getId(), context);
         final List<String> to = new ArrayList<String>();
         to.add(account.getEmail());
@@ -89,7 +96,7 @@ public class EmailInvoiceNotifier implements InvoiceNotifier {
         final HtmlInvoice htmlInvoice;
         try {
             htmlInvoice = generator.generateInvoice(account, invoice, manualPay, internalTenantContext);
-        } catch (IOException e) {
+        } catch (final IOException e) {
             throw new InvoiceApiException(e, ErrorCode.EMAIL_SENDING_FAILED);
         }
 
@@ -102,9 +109,9 @@ public class EmailInvoiceNotifier implements InvoiceNotifier {
         final EmailSender sender = new DefaultEmailSender(config);
         try {
             sender.sendHTMLEmail(to, cc, subject, htmlInvoice.getBody());
-        } catch (EmailApiException e) {
+        } catch (final EmailApiException e) {
             throw new InvoiceApiException(e, ErrorCode.EMAIL_SENDING_FAILED);
-        } catch (IOException e) {
+        } catch (final IOException e) {
             throw new InvoiceApiException(e, ErrorCode.EMAIL_SENDING_FAILED);
         }
     }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AccountJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AccountJson.java
index 3ea7daf..91763c0 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AccountJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AccountJson.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -23,7 +25,6 @@ import java.util.UUID;
 import javax.annotation.Nullable;
 
 import org.joda.time.DateTimeZone;
-
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountData;
 import org.killbill.billing.catalog.api.Currency;
@@ -31,7 +32,6 @@ import org.killbill.billing.util.audit.AccountAuditLogs;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.google.common.base.Objects;
 import com.google.common.base.Strings;
 import com.wordnik.swagger.annotations.ApiModelProperty;
 
@@ -74,7 +74,7 @@ public class AccountJson extends JsonBase {
         this.billCycleDayLocal = account.getBillCycleDayLocal();
         this.currency = account.getCurrency() != null ? account.getCurrency().toString() : null;
         this.paymentMethodId = account.getPaymentMethodId() != null ? account.getPaymentMethodId().toString() : null;
-        this.timeZone = account.getTimeZone().toString();
+        this.timeZone = account.getTimeZone() != null ? account.getTimeZone().toString() : null;
         this.address1 = account.getAddress1();
         this.address2 = account.getAddress2();
         this.postalCode = account.getPostalCode();
@@ -141,7 +141,11 @@ public class AccountJson extends JsonBase {
         return new AccountData() {
             @Override
             public DateTimeZone getTimeZone() {
-                return (Strings.emptyToNull(timeZone) != null) ? DateTimeZone.forID(timeZone) : null;
+                if (Strings.emptyToNull(timeZone) == null) {
+                    return null;
+                } else {
+                    return DateTimeZone.forID(timeZone);
+                }
             }
 
             @Override
@@ -161,17 +165,21 @@ public class AccountJson extends JsonBase {
 
             @Override
             public Boolean isMigrated() {
-                return Objects.firstNonNull(isMigrated, false);
+                return isMigrated;
             }
 
             @Override
             public Boolean isNotifiedForInvoices() {
-                return Objects.firstNonNull(isNotifiedForInvoices, false);
+                return isNotifiedForInvoices;
             }
 
             @Override
             public UUID getPaymentMethodId() {
-                return paymentMethodId != null ? UUID.fromString(paymentMethodId) : null;
+                if (Strings.emptyToNull(paymentMethodId) == null) {
+                    return null;
+                } else {
+                    return UUID.fromString(paymentMethodId);
+                }
             }
 
             @Override
@@ -186,13 +194,7 @@ public class AccountJson extends JsonBase {
 
             @Override
             public Integer getFirstNameLength() {
-                if (firstNameLength == null && name == null) {
-                    return 0;
-                } else if (firstNameLength == null) {
-                    return name.length();
-                } else {
-                    return firstNameLength;
-                }
+                return firstNameLength;
             }
 
             @Override
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
index 707deff..bb57cb7 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
@@ -304,9 +304,6 @@ public class AccountResource extends JaxRsResourceBase {
                                   @javax.ws.rs.core.Context final HttpServletRequest request,
                                   @javax.ws.rs.core.Context final UriInfo uriInfo) throws AccountApiException {
         verifyNonNullOrEmpty(json, "AccountJson body should be specified");
-        // Permit blank values, see https://github.com/killbill/killbill/issues/270
-        verifyNonNull(json.getName(), "AccountJson name needs to be set");
-        verifyNonNull(json.getEmail(), "AccountJson email needs to be set");
 
         final AccountData data = json.toAccountData();
         final Account account = accountUserApi.createAccount(data, context.createContext(createdBy, reason, comment, request));
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java b/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
index ab8e2ba..e84cdb6 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
@@ -74,6 +74,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Predicate;
+import com.google.common.base.Strings;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
@@ -339,6 +340,11 @@ public class OverdueStateApplicator {
             return;
         }
 
+        if (Strings.emptyToNull(account.getEmail()) == null) {
+            log.warn("Unable to send overdue notification email for account {} and overdueable {}: no email specified", account.getId(), account.getId());
+            return;
+        }
+
         final List<String> to = ImmutableList.<String>of(account.getEmail());
         // TODO - should we look at the account CC: list?
         final List<String> cc = ImmutableList.<String>of();
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccount.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccount.java
index c033337..60072e8 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccount.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccount.java
@@ -49,6 +49,16 @@ import static org.testng.Assert.fail;
 
 public class TestAccount extends TestJaxrsBase {
 
+    @Test(groups = "slow", description = "Verify no PII data is required")
+    public void testEmptyAccount() throws Exception {
+        final Account emptyAccount = new Account();
+
+        final Account account = killBillClient.createAccount(emptyAccount, createdBy, reason, comment);
+        Assert.assertNotNull(account.getExternalKey());
+        Assert.assertNull(account.getName());
+        Assert.assertNull(account.getEmail());
+    }
+
     @Test(groups = "slow", description = "Verify external key is unique")
     public void testUniqueExternalKey() throws Exception {
         // Verify the external key is not mandatory