killbill-memoizeit

Merging ErrorCode

1/11/2012 8:59:47 PM

Changes

account/pom.xml 2(+1 -1)

api/pom.xml 2(+1 -1)

beatrix/pom.xml 2(+1 -1)

catalog/pom.xml 2(+1 -1)

invoice/pom.xml 2(+1 -1)

payment/pom.xml 2(+1 -1)

pom.xml 2(+1 -1)

util/pom.xml 2(+1 -1)

Details

account/pom.xml 2(+1 -1)

diff --git a/account/pom.xml b/account/pom.xml
index 2107a34..a55ee85 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.21-SNAPSHOT</version>
+        <version>0.1.1-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-account</artifactId>
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 1c7a397..b953707 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
@@ -133,7 +133,9 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
 
     @Override
     public void addTags(List<Tag> tags) {
-        this.tags.add(tags);
+        if (tags != null) {
+            this.tags.add(tags);
+        }
     }
 
     @Override
@@ -160,4 +162,4 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
     public BigDecimal getBalance() {
         return balance;
     }
-}
+}
\ No newline at end of file
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 a20a519..d6bf995 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
@@ -19,33 +19,47 @@ package com.ning.billing.account.api.user;
 import java.util.List;
 import java.util.UUID;
 import com.google.inject.Inject;
+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.DefaultAccount;
 import com.ning.billing.account.dao.AccountDao;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.tag.Tag;
 
 public class DefaultAccountUserApi implements com.ning.billing.account.api.AccountUserApi {
     private final AccountDao dao;
 
     @Inject
-    public DefaultAccountUserApi(AccountDao dao) {
+    public DefaultAccountUserApi(final AccountDao dao) {
         this.dao = dao;
     }
 
     @Override
-    public Account createAccount(AccountData data) {
-        Account account = new DefaultAccount(data);
-        dao.save(account);
-        return account;
+    public Account createAccount(final AccountData data, final List<CustomField> fields, List<Tag> tags) throws AccountApiException {
+        String key = data.getExternalKey();
+        Account existingAccount = dao.getAccountByKey(key);
+
+        if (existingAccount == null) {
+            Account account = new DefaultAccount(data);
+            account.addFields(fields);
+            account.addTags(tags);
+
+            dao.create(account);
+            return account;
+        } else {
+            throw new AccountApiException(ErrorCode.ACCOUNT_ALREADY_EXISTS, key);
+        }
     }
 
     @Override
-    public Account getAccountByKey(String key) {
+    public Account getAccountByKey(final String key) {
         return dao.getAccountByKey(key);
     }
 
     @Override
-    public Account getAccountById(UUID id) {
+    public Account getAccountById(final UUID id) {
         return dao.getById(id.toString());
     }
 
@@ -55,12 +69,12 @@ public class DefaultAccountUserApi implements com.ning.billing.account.api.Accou
     }
 
     @Override
-    public UUID getIdFromKey(String externalKey) {
+    public UUID getIdFromKey(final String externalKey) {
         return dao.getIdFromKey(externalKey);
     }
 
     @Override
-    public void saveAccount(Account account) {
-        dao.save(account);
+    public void updateAccount(final Account account) {
+        dao.update(account);
     }
 }
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 20eb90b..21122e2 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
@@ -17,6 +17,7 @@
 package com.ning.billing.account.dao;
 
 import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.user.AccountBuilder;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.util.UuidMapper;
@@ -56,7 +57,11 @@ public interface AccountSqlDao extends EntityDao<Account>, Transactional<Account
 
     @Override
     @SqlUpdate
-    public void save(@AccountBinder Account account);
+    public void create(@AccountBinder Account account);
+
+    @Override
+    @SqlUpdate
+    public void update(@AccountBinder Account account);
 
     public static class AccountMapper implements ResultSetMapper<Account> {
         @Override
diff --git a/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java b/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java
index c108bb4..c4671ea 100644
--- a/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java
@@ -50,27 +50,10 @@ public class DefaultAccountDao implements AccountDao {
             @Override
             public Account inTransaction(AccountSqlDao accountSqlDao, TransactionStatus status) throws Exception {
                 Account account = accountSqlDao.getAccountByKey(key);
-
                 if (account != null) {
-                    FieldStoreDao fieldStoreDao = accountSqlDao.become(FieldStoreDao.class);
-                    List<CustomField> fields = fieldStoreDao.load(account.getId().toString(), account.getObjectName());
-
-                    account.clearFields();
-                    if (fields != null) {
-                        for (CustomField field : fields) {
-                            account.setFieldValue(field.getName(), field.getValue());
-                        }
-                    }
-
-                    TagStoreDao tagStoreDao = fieldStoreDao.become(TagStoreDao.class);
-                    List<Tag> tags = tagStoreDao.load(account.getId().toString(), account.getObjectName());
-                    account.clearTags();
-
-                    if (tags != null) {
-                        account.addTags(tags);
-                    }
+                    setCustomFieldsFromWithinTransaction(account, accountSqlDao);
+                    setTagsFromWithinTransaction(account, accountSqlDao);
                 }
-
                 return account;
             }
         });
@@ -87,71 +70,97 @@ public class DefaultAccountDao implements AccountDao {
             @Override
             public Account inTransaction(AccountSqlDao accountSqlDao, TransactionStatus status) throws Exception {
                 Account account = accountSqlDao.getById(id);
-
                 if (account != null) {
-                    FieldStoreDao fieldStoreDao = accountSqlDao.become(FieldStoreDao.class);
-                    List<CustomField> fields = fieldStoreDao.load(account.getId().toString(), account.getObjectName());
-
-                    account.clearFields();
-                    if (fields != null) {
-                        for (CustomField field : fields) {
-                            account.setFieldValue(field.getName(), field.getValue());
-                        }
-                    }
-
-                    TagStoreDao tagStoreDao = fieldStoreDao.become(TagStoreDao.class);
-                    List<Tag> tags = tagStoreDao.load(account.getId().toString(), account.getObjectName());
-                    account.clearTags();
-
-                    if (tags != null) {
-                        account.addTags(tags);
-                    }
+                    setCustomFieldsFromWithinTransaction(account, accountSqlDao);
+                    setTagsFromWithinTransaction(account, accountSqlDao);
                 }
-
                 return account;
             }
         });
     }
 
+
     @Override
     public List<Account> get() {
         return accountDao.get();
     }
 
     @Override
-    public void test() {
-        accountDao.test();
+    public void create(final Account account) {
+        final String accountId = account.getId().toString();
+        final String objectType = DefaultAccount.OBJECT_TYPE;
+
+        accountDao.inTransaction(new Transaction<Void, AccountSqlDao>() {
+            @Override
+            public Void inTransaction(AccountSqlDao accountSqlDao, TransactionStatus status) throws Exception {
+                accountSqlDao.create(account);
+
+                FieldStoreDao fieldStoreDao = accountSqlDao.become(FieldStoreDao.class);
+                fieldStoreDao.save(accountId, objectType, account.getFieldList());
+
+                TagStoreDao tagStoreDao = accountSqlDao.become(TagStoreDao.class);
+                tagStoreDao.save(accountId, objectType, account.getTagList());
+
+                AccountCreationNotification creationEvent = new DefaultAccountCreationEvent(account);
+                eventBus.post(creationEvent);
+                return null;
+            }
+        });
     }
 
     @Override
-    public void save(final Account account) {
+    public void update(final Account account) {
         final String accountId = account.getId().toString();
         final String objectType = DefaultAccount.OBJECT_TYPE;
 
         accountDao.inTransaction(new Transaction<Void, AccountSqlDao>() {
             @Override
-            public Void inTransaction(AccountSqlDao accountDao, TransactionStatus status) throws Exception {
-                Account currentAccount = accountDao.getById(accountId);
-                accountDao.save(account);
+            public Void inTransaction(AccountSqlDao accountSqlDao, TransactionStatus status) throws Exception {
+                Account currentAccount = accountSqlDao.getById(accountId);
 
-                FieldStoreDao fieldStoreDao = accountDao.become(FieldStoreDao.class);
+                accountSqlDao.update(account);
+
+                FieldStoreDao fieldStoreDao = accountSqlDao.become(FieldStoreDao.class);
+                fieldStoreDao.clear(accountId, objectType);
                 fieldStoreDao.save(accountId, objectType, account.getFieldList());
 
                 TagStoreDao tagStoreDao = fieldStoreDao.become(TagStoreDao.class);
+                tagStoreDao.clear(accountId, objectType);
                 tagStoreDao.save(accountId, objectType, account.getTagList());
 
-                if (currentAccount == null) {
-                    AccountCreationNotification creationEvent = new DefaultAccountCreationEvent(account);
-                    eventBus.post(creationEvent);
-                } else {
-                    AccountChangeNotification changeEvent = new DefaultAccountChangeNotification(account.getId(), currentAccount, account);
-                    if (changeEvent.hasChanges()) {
-                        eventBus.post(changeEvent);
-                    }
+                AccountChangeNotification changeEvent = new DefaultAccountChangeNotification(account.getId(), currentAccount, account);
+                if (changeEvent.hasChanges()) {
+                    eventBus.post(changeEvent);
                 }
-
                 return null;
             }
         });
     }
+
+    @Override
+    public void test() {
+        accountDao.test();
+    }
+
+    private void setCustomFieldsFromWithinTransaction(final Account account, final AccountSqlDao transactionalDao) {
+        FieldStoreDao fieldStoreDao = transactionalDao.become(FieldStoreDao.class);
+        List<CustomField> fields = fieldStoreDao.load(account.getId().toString(), account.getObjectName());
+
+        account.clearFields();
+        if (fields != null) {
+            for (CustomField field : fields) {
+                account.setFieldValue(field.getName(), field.getValue());
+            }
+        }
+    }
+
+    private void setTagsFromWithinTransaction(final Account account, final AccountSqlDao transactionalDao) {
+        TagStoreDao tagStoreDao = transactionalDao.become(TagStoreDao.class);
+        List<Tag> tags = tagStoreDao.load(account.getId().toString(), account.getObjectName());
+        account.clearTags();
+
+        if (tags != null) {
+            account.addTags(tags);
+        }
+    }
 }
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 3e0ebdd..7c0e76e 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
@@ -1,13 +1,17 @@
 group AccountDaoSql;
 
-save() ::= <<
+create() ::= <<
     INSERT INTO accounts
     (id, external_key, email, name, first_name_length, phone, currency, billing_cycle_day, payment_provider_name)
     VALUES
-    (:id, :externalKey, :email, :name, :firstNameLength, :phone, :currency, :billingCycleDay, :paymentProviderName)
-    ON DUPLICATE KEY UPDATE
-      external_key = :externalKey, email = :email, name = :name, first_name_length = :firstNameLength,
-      phone = :phone, currency = :currency, payment_provider_name = :paymentProviderName;
+    (:id, :externalKey, :email, :name, :firstNameLength, :phone, :currency, :billingCycleDay, :paymentProviderName);
+>>
+
+update() ::= <<
+    UPDATE accounts
+    SET email = :email, name = :name, first_name_length = :firstNameLength, phone = :phone,
+        currency = :currency, billing_cycle_day = :billingCycleDay, payment_provider_name = :paymentProviderName
+    WHERE id = :id;
 >>
 
 getAccountByKey() ::= <<
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 acca205..ccfed72 100644
--- a/account/src/main/resources/com/ning/billing/account/ddl.sql
+++ b/account/src/main/resources/com/ning/billing/account/ddl.sql
@@ -12,42 +12,4 @@ CREATE TABLE accounts (
     PRIMARY KEY(id)
 ) ENGINE=innodb;
 CREATE UNIQUE INDEX accounts_external_key ON accounts(external_key);
-CREATE UNIQUE INDEX accounts_email ON accounts(email);
-
-DROP TABLE IF EXISTS custom_fields;
-CREATE TABLE custom_fields (
-  id char(36) NOT NULL,
-  object_id char(36) NOT NULL,
-  object_type varchar(30) NOT NULL,
-  field_name varchar(30) NOT NULL,
-  field_value varchar(255) NOT NULL,
-  PRIMARY KEY(id)
-) ENGINE=innodb;
-CREATE INDEX custom_fields_object_id_object_type ON custom_fields(object_id, object_type);
-CREATE UNIQUE INDEX custom_fields_unique ON custom_fields(object_id, object_type, field_name);
-
-DROP TABLE IF EXISTS tag_descriptions;
-CREATE TABLE tag_descriptions (
-  id char(36) NOT NULL,
-  name varchar(20) NOT NULL,
-  created_by varchar(50) NOT NULL,
-  creation_date datetime NOT NULL,
-  description varchar(200) NOT NULL,
-  generate_invoice boolean DEFAULT false,
-  process_payment boolean DEFAULT false,
-  PRIMARY KEY(id)
-) ENGINE=innodb;
-CREATE UNIQUE INDEX tag_descriptions_name ON tag_descriptions(name);
-
-DROP TABLE IF EXISTS tags;
-CREATE TABLE tags (
-  id char(36) NOT NULL,
-  tag_description_id char(36) NOT NULL,
-  object_id char(36) NOT NULL,
-  object_type varchar(30) NOT NULL,
-  date_added datetime NOT NULL,
-  added_by varchar(50) NOT NULL,
-  PRIMARY KEY(id)
-) ENGINE = innodb;
-CREATE INDEX tags_by_object ON tags(object_id);
-CREATE UNIQUE INDEX tags_unique ON tags(tag_description_id, object_id);
\ No newline at end of file
+CREATE UNIQUE INDEX accounts_email ON accounts(email);
\ No newline at end of file
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 77efe00..b18a41b 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
@@ -38,14 +38,17 @@ public abstract class AccountDaoTestBase {
 
     @BeforeClass(alwaysRun = true)
     protected void setup() throws IOException {
-        // Healthcheck test to make sure MySQL is setup properly
+        // Health check test to make sure MySQL is setup properly
         try {
             module = new AccountModuleMock();
             final String accountDdl = IOUtils.toString(AccountSqlDao.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
             final String invoiceDdl = IOUtils.toString(AccountSqlDao.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
+            final String utilDdl = IOUtils.toString(AccountSqlDao.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
+
             module.startDb();
             module.initDb(accountDdl);
             module.initDb(invoiceDdl);
+            module.initDb(utilDdl);
 
             final Injector injector = Guice.createInjector(Stage.DEVELOPMENT, module);
             dbi = injector.getInstance(IDBI.class);
diff --git a/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java
index 7a82f98..a4b0b98 100644
--- a/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java
@@ -21,6 +21,7 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.testng.annotations.Test;
 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.DefaultAccount;
 import com.ning.billing.util.tag.DefaultTagDescription;
@@ -30,7 +31,6 @@ import com.ning.billing.account.api.user.AccountBuilder;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.util.clock.DefaultClock;
 import com.ning.billing.util.tag.dao.TagDescriptionDao;
-import com.ning.billing.util.tag.dao.TagDescriptionDao;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
@@ -57,7 +57,7 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
     public void testBasic() {
 
         Account a = createTestAccount();
-        accountDao.save(a);
+        accountDao.create(a);
         String key = a.getExternalKey();
 
         Account r = accountDao.getAccountByKey(key);
@@ -74,14 +74,14 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
     }
 
     @Test
-    public void testGetById() {
+    public void testGetById() throws AccountApiException {
         Account account = createTestAccount();
         UUID id = account.getId();
         String key = account.getExternalKey();
         String name = account.getName();
         int firstNameLength = account.getFirstNameLength();
 
-        accountDao.save(account);
+        accountDao.create(account);
 
         account = accountDao.getById(id.toString());
         assertNotNull(account);
@@ -99,7 +99,7 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
         String fieldValue = "testField1_value";
         account.setFieldValue(fieldName, fieldValue);
 
-        accountDao.save(account);
+        accountDao.create(account);
 
         Account thisAccount = accountDao.getAccountByKey(account.getExternalKey());
         assertNotNull(thisAccount);
@@ -112,13 +112,13 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
         Account account = createTestAccount();
         TagDescription description = new DefaultTagDescription("Test Tag", "For testing only", true, true, "Test System", new DateTime());
         TagDescriptionDao tagDescriptionDao = dbi.onDemand(TagDescriptionDao.class);
-        tagDescriptionDao.save(description);
+        tagDescriptionDao.create(description);
 
         String addedBy = "testTags()";
         DateTime dateAdded = new DefaultClock().getUTCNow();
         account.addTag(description, addedBy, dateAdded);
         assertEquals(account.getTagList().size(), 1);
-        accountDao.save(account);
+        accountDao.create(account);
 
         Account thisAccount = accountDao.getById(account.getId().toString());
         List<Tag> tagList = thisAccount.getTagList();
@@ -135,16 +135,16 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
     @Test
     public void testGetIdFromKey() {
         Account account = createTestAccount();
-        accountDao.save(account);
+        accountDao.create(account);
 
         UUID accountId = accountDao.getIdFromKey(account.getExternalKey());
         assertEquals(accountId, account.getId());
     }
 
     @Test
-    public void testUpdate() {
+    public void testUpdate() throws AccountApiException {
         final Account account = createTestAccount();
-        accountDao.save(account);
+        accountDao.create(account);
 
         AccountData accountData = new AccountData() {
             @Override
@@ -182,7 +182,7 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
         };
 
         Account updatedAccount = new DefaultAccount(account.getId(), accountData);
-        accountDao.save(updatedAccount);
+        accountDao.update(updatedAccount);
 
         Account savedAccount = accountDao.getAccountByKey(account.getExternalKey());
 
diff --git a/analytics/pom.xml b/analytics/pom.xml
index 03e7d72..31c7952 100644
--- a/analytics/pom.xml
+++ b/analytics/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.21-SNAPSHOT</version>
+        <version>0.1.1-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-analytics</artifactId>
diff --git a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
index 262e9d9..d623338 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
@@ -18,6 +18,7 @@ package com.ning.billing.analytics;
 
 import com.google.common.eventbus.Subscribe;
 import com.google.inject.Inject;
+import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountChangeNotification;
 import com.ning.billing.account.api.AccountCreationNotification;
 import com.ning.billing.entitlement.api.user.SubscriptionTransition;
@@ -35,8 +36,7 @@ public class AnalyticsListener
     }
 
     @Subscribe
-    public void handleSubscriptionTransitionChange(final SubscriptionTransition event)
-    {
+    public void handleSubscriptionTransitionChange(final SubscriptionTransition event) throws AccountApiException {
         switch (event.getTransitionType()) {
             case MIGRATE_ENTITLEMENT:
                 // TODO do nothing for now
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java
index 33b8f4d..0384fab 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java
@@ -18,6 +18,7 @@ package com.ning.billing.analytics;
 
 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.AccountUserApi;
 import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionDao;
 import com.ning.billing.catalog.api.Currency;
@@ -46,44 +47,37 @@ public class BusinessSubscriptionTransitionRecorder
         this.accountApi = accountApi;
     }
 
-    public void subscriptionCreated(final SubscriptionTransition created)
-    {
+    public void subscriptionCreated(final SubscriptionTransition created) throws AccountApiException {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCreated(created.getNextPlan());
         recordTransition(event, created);
     }
 
-    public void subscriptionCancelled(final SubscriptionTransition cancelled)
-    {
+    public void subscriptionCancelled(final SubscriptionTransition cancelled) throws AccountApiException {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCancelled(cancelled.getNextPlan());
         recordTransition(event, cancelled);
     }
 
-    public void subscriptionChanged(final SubscriptionTransition changed)
-    {
+    public void subscriptionChanged(final SubscriptionTransition changed) throws AccountApiException {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionChanged(changed.getNextPlan());
         recordTransition(event, changed);
     }
 
-    public void subscriptionPaused(final SubscriptionTransition paused)
-    {
+    public void subscriptionPaused(final SubscriptionTransition paused) throws AccountApiException {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPaused(paused.getNextPlan());
         recordTransition(event, paused);
     }
 
-    public void subscriptionResumed(final SubscriptionTransition resumed)
-    {
+    public void subscriptionResumed(final SubscriptionTransition resumed) throws AccountApiException {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionResumed(resumed.getNextPlan());
         recordTransition(event, resumed);
     }
 
-    public void subscriptionPhaseChanged(final SubscriptionTransition phaseChanged)
-    {
+    public void subscriptionPhaseChanged(final SubscriptionTransition phaseChanged) throws AccountApiException {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPhaseChanged(phaseChanged.getNextPlan(), phaseChanged.getNextState());
         recordTransition(event, phaseChanged);
     }
 
-    public void recordTransition(final BusinessSubscriptionEvent event, final SubscriptionTransition transition)
-    {
+    public void recordTransition(final BusinessSubscriptionEvent event, final SubscriptionTransition transition) throws AccountApiException {
         Currency currency = null;
         String transitionKey = null;
         String accountKey = null;
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 03a27d0..c146de7 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
@@ -48,7 +48,9 @@ import com.ning.billing.entitlement.api.user.SubscriptionTransitionData;
 import com.ning.billing.entitlement.events.EntitlementEvent;
 import com.ning.billing.entitlement.events.user.ApiEventType;
 import com.ning.billing.util.eventbus.EventBus;
+import com.ning.billing.util.tag.DefaultTag;
 import com.ning.billing.util.tag.DefaultTagDescription;
+import com.ning.billing.util.tag.Tag;
 import com.ning.billing.util.tag.dao.TagDescriptionDao;
 import org.apache.commons.io.IOUtils;
 import org.joda.time.DateTime;
@@ -61,13 +63,17 @@ import org.testng.annotations.Test;
 
 import java.io.IOException;
 import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.UUID;
 
+import static org.testng.Assert.fail;
+
 @Guice(modules = AnalyticsTestModule.class)
 public class TestAnalyticsService
 {
-    private static final String KEY = "1234";
-    private static final String ACCOUNT_KEY = "pierre-1234";
+    private static final String KEY = "12345";
+    private static final String ACCOUNT_KEY = "pierre-12345";
     private static final DefaultTagDescription TAG_ONE = new DefaultTagDescription("batch20", "something", false, false, "pierre", new DateTime(DateTimeZone.UTC));
     private static final DefaultTagDescription TAG_TWO = new DefaultTagDescription("awesome", "something", false, false, "pierre", new DateTime(DateTimeZone.UTC));
 
@@ -106,18 +112,23 @@ public class TestAnalyticsService
         // Killbill generic setup
         setupBusAndMySQL();
 
-        tagDao.save(TAG_ONE);
-        tagDao.save(TAG_TWO);
+        tagDao.create(TAG_ONE);
+        tagDao.create(TAG_TWO);
 
         final MockAccount account = new MockAccount(UUID.randomUUID(), ACCOUNT_KEY, Currency.USD);
-        final Account storedAccount = accountApi.createAccount(account);
-        storedAccount.addTag(TAG_ONE, "pierre", new DateTime(DateTimeZone.UTC));
-        storedAccount.addTag(TAG_TWO, "pierre", new DateTime(DateTimeZone.UTC));
-        accountApi.saveAccount(storedAccount);
-
-        // Create events for the bus and expected results
-        createSubscriptionTransitionEvent(storedAccount);
-        createAccountCreationEvent(storedAccount);
+        try {
+            List<Tag> tags = new ArrayList<Tag>();
+            tags.add(new DefaultTag(TAG_ONE, "pierre", new DateTime(DateTimeZone.UTC)));
+            tags.add(new DefaultTag(TAG_TWO, "pierre", new DateTime(DateTimeZone.UTC)));
+
+            final Account storedAccount = accountApi.createAccount(account, null, tags);
+
+            // Create events for the bus and expected results
+            createSubscriptionTransitionEvent(storedAccount);
+            createAccountCreationEvent(storedAccount);
+        } catch (Throwable t) {
+            fail("Initializing accounts failed.", t);
+        }
     }
 
     private void setupBusAndMySQL() throws IOException
@@ -127,11 +138,13 @@ public class TestAnalyticsService
         final String analyticsDdl = IOUtils.toString(BusinessSubscriptionTransitionDao.class.getResourceAsStream("/com/ning/billing/analytics/ddl.sql"));
         final String accountDdl = IOUtils.toString(BusinessSubscriptionTransitionDao.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
         final String entitlementDdl = IOUtils.toString(BusinessSubscriptionTransitionDao.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
+        final String utilDdl = IOUtils.toString(BusinessSubscriptionTransitionDao.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
 
         helper.startMysql();
         helper.initDb(analyticsDdl);
         helper.initDb(accountDdl);
         helper.initDb(entitlementDdl);
+        helper.initDb(utilDdl);
     }
 
     private void createSubscriptionTransitionEvent(final Account account) throws EntitlementUserApiException
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 faf4dc8..3b38c8c 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java
@@ -109,6 +109,11 @@ public class MockAccount implements Account
     }
 
     @Override
+    public void addFields(List<CustomField> fields) {
+        throw new NotImplementedException();
+    }
+
+    @Override
     public void clearFields() {
         throw new NotImplementedException();
     }
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java b/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java
index 4c39456..791d191 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java
@@ -21,10 +21,13 @@ import sun.reflect.generics.reflectiveObjects.NotImplementedException;
 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.AccountData;
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.account.api.DefaultAccount;
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.tag.Tag;
 
 public class MockIAccountUserApi implements AccountUserApi
 {
@@ -38,14 +41,14 @@ public class MockIAccountUserApi implements AccountUserApi
     }
 
     @Override
-    public Account createAccount(final AccountData data)
+    public Account createAccount(final AccountData data, final List<CustomField> fields, final List<Tag> tags)
     {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public void saveAccount(Account account) {
-        throw new NotImplementedException();
+    public void updateAccount(final Account account) {
+        throw new UnsupportedOperationException();
     }
 
     @Override
@@ -55,8 +58,7 @@ public class MockIAccountUserApi implements AccountUserApi
     }
 
     @Override
-    public Account getAccountById(final UUID uid)
-    {
+    public Account getAccountById(final UUID uid) {
         return new DefaultAccount(account);
     }
 

api/pom.xml 2(+1 -1)

diff --git a/api/pom.xml b/api/pom.xml
index a998c67..8d6d214 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.21-SNAPSHOT</version>
+        <version>0.1.1-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-api</artifactId>
diff --git a/api/src/main/java/com/ning/billing/account/api/AccountApiException.java b/api/src/main/java/com/ning/billing/account/api/AccountApiException.java
new file mode 100644
index 0000000..d9761b6
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/account/api/AccountApiException.java
@@ -0,0 +1,34 @@
+/*
+ * 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.BillingExceptionBase;
+import com.ning.billing.ErrorCode;
+
+public class AccountApiException extends BillingExceptionBase {
+    public AccountApiException(Throwable cause, int code, final String msg) {
+        super(cause, code, msg);
+    }
+
+    public AccountApiException(Throwable cause, ErrorCode code, final Object... args) {
+        super(cause, code, args);
+    }
+
+    public AccountApiException(ErrorCode code, final Object... args) {
+        super(code, args);
+    }
+}
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 b34d9a3..a535ada 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
@@ -18,12 +18,14 @@ package com.ning.billing.account.api;
 
 import java.util.List;
 import java.util.UUID;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.tag.Tag;
 
 public interface AccountUserApi {
 
-    public Account createAccount(AccountData data);
+    public Account createAccount(AccountData data, List<CustomField> fields, List<Tag> tags) throws AccountApiException;
 
-    public void saveAccount(Account account);
+    public void updateAccount(Account account);
 
     public Account getAccountByKey(String key);
 
diff --git a/api/src/main/java/com/ning/billing/catalog/api/Currency.java b/api/src/main/java/com/ning/billing/catalog/api/Currency.java
index 0e7cd7d..3bef242 100644
--- a/api/src/main/java/com/ning/billing/catalog/api/Currency.java
+++ b/api/src/main/java/com/ning/billing/catalog/api/Currency.java
@@ -31,7 +31,7 @@ public enum Currency {
 //	CAD,
 //	JPY
 
-    public Currency getDefaultCurrency() {
+    public static Currency getDefaultCurrency() {
         return Currency.USD;
     }
 }
diff --git a/api/src/main/java/com/ning/billing/ErrorCode.java b/api/src/main/java/com/ning/billing/ErrorCode.java
index cdf2c77..254df9f 100644
--- a/api/src/main/java/com/ning/billing/ErrorCode.java
+++ b/api/src/main/java/com/ning/billing/ErrorCode.java
@@ -85,19 +85,25 @@ public enum ErrorCode {
      */
     CAT_NO_SUCH_PHASE(2040,"Could not find any phases named '%s'"),
     CAT_BAD_PHASE_NAME(2041,"Bad phase name '%s'"),
-
     /*
      * Versioned Catalog
      */
     CAT_NO_CATALOG_FOR_GIVEN_DATE(2050, "There is no catalog version that applies for the given date '%s'"),
     CAT_NO_CATALOG_ENTRIES_FOR_GIVEN_DATE(2051, "The are no catalog entries that apply for the given date '%s'"),
-    CAT_CATALOG_NAME_MISMATCH(2052, "The catalog name '%s' does not match the name of the catalog we are trying to add '%s'"),
-    
+    CAT_CATALOG_NAME_MISMATCH(2052, "The catalog name '%s' does not match the name of the catalog we are trying to add '%s'"),  
     /*
      * Billing Alignment
      */
-    CAT_INVALID_BILLING_ALIGNMENT(2060, "Invalid billing alignment '%s'")
+    CAT_INVALID_BILLING_ALIGNMENT(2060, "Invalid billing alignment '%s'"),
     
+   /*
+    *
+    * Range 3000 : ACCOUNT
+    *
+    */
+    ACCOUNT_ALREADY_EXISTS(3000, "Account already exists for key %s"),
+    ACCOUNT_INVALID_NAME(3001, "An invalid name was specified when creating or updating an account.")
+
     ;
     private int code;
     private String format;
diff --git a/api/src/main/java/com/ning/billing/util/customfield/CustomizableEntity.java b/api/src/main/java/com/ning/billing/util/customfield/CustomizableEntity.java
index 7b8f37a..e21fd83 100644
--- a/api/src/main/java/com/ning/billing/util/customfield/CustomizableEntity.java
+++ b/api/src/main/java/com/ning/billing/util/customfield/CustomizableEntity.java
@@ -26,6 +26,8 @@ public interface CustomizableEntity extends Entity {
 
     public List<CustomField> getFieldList();
 
+    public void addFields(List<CustomField> fields);
+
     public void clearFields();
 
     public String getObjectName();

beatrix/pom.xml 2(+1 -1)

diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index ec79685..c17fe1d 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.21-SNAPSHOT</version>
+        <version>0.1.1-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-beatrix</artifactId>

catalog/pom.xml 2(+1 -1)

diff --git a/catalog/pom.xml b/catalog/pom.xml
index 3d69bd5..a435701 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.21-SNAPSHOT</version>
+        <version>0.1.1-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-catalog</artifactId>
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index a698d45..ded9606 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.21-SNAPSHOT</version>
+        <version>0.1.1-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-entitlement</artifactId>

invoice/pom.xml 2(+1 -1)

diff --git a/invoice/pom.xml b/invoice/pom.xml
index 2434c2d..d694808 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.21-SNAPSHOT</version>
+        <version>0.1.1-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-invoice</artifactId>
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 1186456..20f33c7 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
@@ -16,6 +16,8 @@
 
 package com.ning.billing.invoice.dao;
 
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
 import com.google.inject.Inject;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceCreationNotification;
@@ -40,7 +42,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
     private final static Logger log = LoggerFactory.getLogger(DefaultInvoiceDao.class);
 
     @Inject
-    public DefaultInvoiceDao(IDBI dbi, EventBus eventBus) {
+    public DefaultInvoiceDao(final IDBI dbi, final EventBus eventBus) {
         this.invoiceDao = dbi.onDemand(InvoiceSqlDao.class);
         this.eventBus = eventBus;
     }
@@ -54,7 +56,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
     public List<Invoice> get() {
         return invoiceDao.inTransaction(new Transaction<List<Invoice>, InvoiceSqlDao>() {
              @Override
-             public List<Invoice> inTransaction(InvoiceSqlDao invoiceDao, TransactionStatus status) throws Exception {
+             public List<Invoice> inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
                  List<Invoice> invoices = invoiceDao.get();
 
                  InvoiceItemSqlDao invoiceItemDao = invoiceDao.become(InvoiceItemSqlDao.class);
@@ -72,7 +74,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
     public Invoice getById(final String invoiceId) {
         return invoiceDao.inTransaction(new Transaction<Invoice, InvoiceSqlDao>() {
              @Override
-             public Invoice inTransaction(InvoiceSqlDao invoiceDao, TransactionStatus status) throws Exception {
+             public Invoice inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
                  Invoice invoice = invoiceDao.getById(invoiceId);
 
                  if (invoice != null) {
@@ -87,25 +89,24 @@ public class DefaultInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public void save(final Invoice invoice) {
+    public void create(final Invoice invoice) {
          invoiceDao.inTransaction(new Transaction<Void, InvoiceSqlDao>() {
              @Override
-             public Void inTransaction(InvoiceSqlDao invoiceDao, TransactionStatus status) throws Exception {
+             public Void inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
                 Invoice currentInvoice = invoiceDao.getById(invoice.getId().toString());
-                invoiceDao.save(invoice);
-
-                List<InvoiceItem> invoiceItems = invoice.getItems();
-                InvoiceItemSqlDao invoiceItemDao = invoiceDao.become(InvoiceItemSqlDao.class);
-                invoiceItemDao.save(invoiceItems);
 
                 if (currentInvoice == null) {
+                    invoiceDao.create(invoice);
+
+                    List<InvoiceItem> invoiceItems = invoice.getItems();
+                    InvoiceItemSqlDao invoiceItemDao = invoiceDao.become(InvoiceItemSqlDao.class);
+                    invoiceItemDao.create(invoiceItems);
+
                     InvoiceCreationNotification event;
                     event = new DefaultInvoiceCreationNotification(invoice.getId(), invoice.getAccountId(),
                                                                   invoice.getAmountOutstanding(), invoice.getCurrency(),
                                                                   invoice.getInvoiceDate());
                     eventBus.post(event);
-                } else {
-
                 }
 
                 return null;
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
index b59e4ed..e9306b2 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
@@ -23,7 +23,7 @@ import java.util.UUID;
 import com.ning.billing.invoice.api.Invoice;
 
 public interface InvoiceDao {
-    void save(Invoice invoice);
+    void create(Invoice invoice);
 
     Invoice getById(final String id);
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java
index a89749e..6f4e47b 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java
@@ -50,10 +50,14 @@ public interface InvoiceItemSqlDao extends EntityDao<InvoiceItem> {
 
     @Override
     @SqlUpdate
-    void save(@InvoiceItemBinder final InvoiceItem invoiceItem);
+    void create(@InvoiceItemBinder final InvoiceItem invoiceItem);
+
+    @Override
+    @SqlUpdate
+    void update(@InvoiceItemBinder final InvoiceItem invoiceItem);
 
     @SqlBatch
-    void save(@InvoiceItemBinder final List<InvoiceItem> items);
+    void create(@InvoiceItemBinder final List<InvoiceItem> items);
 
     @BindingAnnotation(InvoiceItemBinder.InvoiceItemBinderFactory.class)
     @Retention(RetentionPolicy.RUNTIME)
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
index aa0051a..9ede145 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
@@ -53,7 +53,11 @@ import java.util.UUID;
 public interface InvoiceSqlDao extends EntityDao<Invoice>, Transactional<InvoiceSqlDao>, Transmogrifier, CloseMe {
     @Override
     @SqlUpdate
-    void save(@InvoiceBinder Invoice invoice);
+    void create(@InvoiceBinder Invoice invoice);
+
+    @Override
+    @SqlUpdate
+    void update(@InvoiceBinder Invoice invoice);
 
     @SqlQuery
     List<Invoice> getInvoicesByAccount(@Bind("accountId") final String accountId);
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java
index 797f587..97f82e0 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java
@@ -126,6 +126,10 @@ public class DefaultInvoice implements Invoice {
 
     @Override
     public boolean isDueForPayment(final DateTime targetDate, final int numberOfDays) {
+        if (getTotalAmount().compareTo(BigDecimal.ZERO) == 0) {
+            return false;
+        }
+
         if (lastPaymentAttempt == null) {
             return true;
         }
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceItemSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
index 490d075..4dc783f 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
@@ -25,12 +25,16 @@ getInvoiceItemsBySubscription() ::= <<
   WHERE subscription_id = :subscriptionId;
 >>
 
-save() ::= <<
+create() ::= <<
   INSERT INTO invoice_items(id, invoice_id, subscription_id, start_date, end_date, description, amount, rate, currency)
-  VALUES(:id, :invoiceId, :subscriptionId, :startDate, :endDate, :description, :amount, :rate, :currency)
-  ON DUPLICATE KEY UPDATE
-    start_date = :startDate, end_date = :endDate, description = :description,
-    amount = :amount, rate = :rate, currency = :currency''
+  VALUES(:id, :invoiceId, :subscriptionId, :startDate, :endDate, :description, :amount, :rate, :currency);
+>>
+
+update() ::= <<
+  UPDATE invoice_items
+  SET invoice_id = :invoiceId, subscription_id = :subscriptionId, start_date = :startDate, end_date = :endDate,
+      description = :description, amount = :amount, rate = :rate, currency = :currency
+  WHERE id = :id;
 >>
 
 test() ::= <<
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
index 0323040..8766e18 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
@@ -52,11 +52,15 @@ getById() ::= <<
   GROUP BY i.id, i.account_id, i.invoice_date, i.target_date, i.currency;
 >>
 
-save() ::= <<
+create() ::= <<
   INSERT INTO invoices(id, account_id, invoice_date, target_date, currency)
-  VALUES (:id, :accountId, :invoiceDate, :targetDate, :currency)
-  ON DUPLICATE KEY UPDATE
-    invoice_date = :invoiceDate, target_date = :targetDate, currency = :currency;
+  VALUES (:id, :accountId, :invoiceDate, :targetDate, :currency);
+>>
+
+update() ::= <<
+  UPDATE invoices
+  SET account_id = :accountId, invoice_date = :invoiceDate, target_date = :targetDate, currency = :currency
+  WHERE id = :id;
 >>
 
 notifySuccessfulPayment() ::= <<
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 0666ada..3fe72ed 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
@@ -45,7 +45,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         Invoice invoice = new DefaultInvoice(accountId, new DefaultClock().getUTCNow(), Currency.USD);
         DateTime invoiceDate = invoice.getInvoiceDate();
 
-        invoiceDao.save(invoice);
+        invoiceDao.create(invoice);
 
         List<Invoice> invoices = invoiceDao.getInvoicesByAccount(accountId.toString());
         assertNotNull(invoices);
@@ -68,7 +68,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         DateTime endDate = new DateTime(2010, 4, 1, 0, 0, 0, 0);
         InvoiceItem invoiceItem = new DefaultInvoiceItem(invoiceId, subscriptionId, startDate, endDate, "test", new BigDecimal("21.00"), new BigDecimal("7.00"), Currency.USD);
         invoice.add(invoiceItem);
-        invoiceDao.save(invoice);
+        invoiceDao.create(invoice);
 
         Invoice savedInvoice = invoiceDao.getById(invoiceId.toString());
         assertNotNull(savedInvoice);
@@ -105,12 +105,12 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         DateTime paymentAttemptDate = new DateTime(2011, 6, 24, 12, 14, 36, 0);
         BigDecimal paymentAmount = new BigDecimal("14.0");
 
-        invoiceDao.save(invoice);
+        invoiceDao.create(invoice);
         invoiceDao.notifySuccessfulPayment(invoice.getId().toString(), paymentAmount, Currency.USD.toString(), paymentId, paymentAttemptDate.toDate());
 
         invoice = invoiceDao.getById(invoice.getId().toString());
-//        assertEquals(invoice.getAmountPaid().compareTo(paymentAmount), 0);
-//        assertTrue(invoice.getLastPaymentAttempt().equals(paymentAttemptDate));
+        assertEquals(invoice.getAmountPaid().compareTo(paymentAmount), 0);
+        assertTrue(invoice.getLastPaymentAttempt().equals(paymentAttemptDate));
     }
 
     @Test
@@ -121,11 +121,11 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
 
         DateTime paymentAttemptDate = new DateTime(2011, 6, 24, 12, 14, 36, 0);
 
-        invoiceDao.save(invoice);
+        invoiceDao.create(invoice);
         invoiceDao.notifyFailedPayment(invoice.getId().toString(), UUID.randomUUID().toString(), paymentAttemptDate.toDate());
 
         invoice = invoiceDao.getById(invoice.getId().toString());
-//        assertTrue(invoice.getLastPaymentAttempt().equals(paymentAttemptDate));
+        assertTrue(invoice.getLastPaymentAttempt().equals(paymentAttemptDate));
     }
 
     @Test
@@ -140,7 +140,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         UUID accountId = UUID.randomUUID();
         Invoice invoice = new DefaultInvoice(accountId, targetDate, Currency.USD);
 
-        invoiceDao.save(invoice);
+        invoiceDao.create(invoice);
         invoices = invoiceDao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
         assertEquals(invoices.size(), existingInvoiceCount);
     }
@@ -163,7 +163,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
 
         DefaultInvoiceItem item = new DefaultInvoiceItem(invoiceId, subscriptionId, targetDate, endDate, "test", amount, rate, Currency.USD);
         invoice.add(item);
-        invoiceDao.save(invoice);
+        invoiceDao.create(invoice);
 
         // ensure that the number of invoices for payment has increased by 1
         int count;
@@ -248,7 +248,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
 
         // create invoice 1 (subscriptions 1-4)
         Invoice invoice1 = new DefaultInvoice(accountId, targetDate, Currency.USD);
-        invoiceDao.save(invoice1);
+        invoiceDao.create(invoice1);
 
         UUID invoiceId1 = invoice1.getId();
 
@@ -256,34 +256,34 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         DateTime endDate = startDate.plusMonths(1);
 
         DefaultInvoiceItem item1 = new DefaultInvoiceItem(invoiceId1, subscriptionId1, startDate, endDate, "test A", rate1, rate1, Currency.USD);
-        invoiceItemDao.save(item1);
+        invoiceItemDao.create(item1);
 
         DefaultInvoiceItem item2 = new DefaultInvoiceItem(invoiceId1, subscriptionId2, startDate, endDate, "test B", rate2, rate2, Currency.USD);
-        invoiceItemDao.save(item2);
+        invoiceItemDao.create(item2);
 
         DefaultInvoiceItem item3 = new DefaultInvoiceItem(invoiceId1, subscriptionId3, startDate, endDate, "test C", rate3, rate3, Currency.USD);
-        invoiceItemDao.save(item3);
+        invoiceItemDao.create(item3);
 
         DefaultInvoiceItem item4 = new DefaultInvoiceItem(invoiceId1, subscriptionId4, startDate, endDate, "test D", rate4, rate4, Currency.USD);
-        invoiceItemDao.save(item4);
+        invoiceItemDao.create(item4);
 
         // create invoice 2 (subscriptions 1-3)
-        DefaultInvoice invoice = new DefaultInvoice(accountId, targetDate, Currency.USD);
-        invoiceDao.save(invoice);
+        DefaultInvoice invoice2 = new DefaultInvoice(accountId, targetDate, Currency.USD);
+        invoiceDao.create(invoice2);
 
-        UUID invoiceId2 = invoice.getId();
+        UUID invoiceId2 = invoice2.getId();
 
         startDate = endDate;
         endDate = startDate.plusMonths(1);
 
         DefaultInvoiceItem item5 = new DefaultInvoiceItem(invoiceId2, subscriptionId1, startDate, endDate, "test A", rate1, rate1, Currency.USD);
-        invoiceItemDao.save(item5);
+        invoiceItemDao.create(item5);
 
         DefaultInvoiceItem item6 = new DefaultInvoiceItem(invoiceId2, subscriptionId2, startDate, endDate, "test B", rate2, rate2, Currency.USD);
-        invoiceItemDao.save(item6);
+        invoiceItemDao.create(item6);
 
         DefaultInvoiceItem item7 = new DefaultInvoiceItem(invoiceId2, subscriptionId3, startDate, endDate, "test C", rate3, rate3, Currency.USD);
-        invoiceItemDao.save(item7);
+        invoiceItemDao.create(item7);
 
         // check that each subscription returns the correct number of invoices
         List<Invoice> items1 = invoiceDao.getInvoicesBySubscription(subscriptionId1.toString());
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
index 0f30753..fa743ff 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
@@ -46,7 +46,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
         BigDecimal rate = new BigDecimal("20.00");
 
         InvoiceItem item = new DefaultInvoiceItem(invoiceId, subscriptionId, startDate, endDate, "test", rate, rate, Currency.USD);
-        invoiceItemDao.save(item);
+        invoiceItemDao.create(item);
 
         InvoiceItem thisItem = invoiceItemDao.getById(item.getId().toString());
         assertNotNull(thisItem);
@@ -70,7 +70,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
         for (int i = 0; i < 3; i++) {
             UUID invoiceId = UUID.randomUUID();
             DefaultInvoiceItem item = new DefaultInvoiceItem(invoiceId, subscriptionId, startDate.plusMonths(i), startDate.plusMonths(i + 1), "test", rate, rate, Currency.USD);
-            invoiceItemDao.save(item);
+            invoiceItemDao.create(item);
         }
 
         List<InvoiceItem> items = invoiceItemDao.getInvoiceItemsBySubscription(subscriptionId.toString());
@@ -87,7 +87,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
             UUID subscriptionId = UUID.randomUUID();
             BigDecimal amount = rate.multiply(new BigDecimal(i + 1));
             DefaultInvoiceItem item = new DefaultInvoiceItem(invoiceId, subscriptionId, startDate, startDate.plusMonths(1), "test", amount, amount, Currency.USD);
-            invoiceItemDao.save(item);
+            invoiceItemDao.create(item);
         }
 
         List<InvoiceItem> items = invoiceItemDao.getInvoiceItemsByInvoice(invoiceId.toString());
@@ -100,7 +100,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
         DateTime targetDate = new DateTime(2011, 5, 23, 0, 0, 0, 0);
         DefaultInvoice invoice = new DefaultInvoice(accountId, targetDate, Currency.USD);
 
-        invoiceDao.save(invoice);
+        invoiceDao.create(invoice);
 
         UUID invoiceId = invoice.getId();
         DateTime startDate = new DateTime(2011, 3, 1, 0, 0, 0, 0);
@@ -108,7 +108,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
 
         UUID subscriptionId = UUID.randomUUID();
         DefaultInvoiceItem item = new DefaultInvoiceItem(invoiceId, subscriptionId, startDate, startDate.plusMonths(1), "test", rate, rate, Currency.USD);
-        invoiceItemDao.save(item);
+        invoiceItemDao.create(item);
 
         List<InvoiceItem> items = invoiceItemDao.getInvoiceItemsByAccount(accountId.toString());
         assertEquals(items.size(), 1);

payment/pom.xml 2(+1 -1)

diff --git a/payment/pom.xml b/payment/pom.xml
index aca06a2..187e5d4 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.21-SNAPSHOT</version>
+        <version>0.1.1-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-payment</artifactId>

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index 21739e9..6cf28d5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,7 +17,7 @@
     <groupId>com.ning.billing</groupId>
     <artifactId>killbill</artifactId>
     <packaging>pom</packaging>
-    <version>0.0.21-SNAPSHOT</version>
+    <version>0.1.1-SNAPSHOT</version>
     <name>killbill</name>
     <description>Library for managing recurring subscriptions and the associated billing</description>
     <url>http://github.com/ning/killbill</url>

util/pom.xml 2(+1 -1)

diff --git a/util/pom.xml b/util/pom.xml
index 5152ab7..7884db7 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.21-SNAPSHOT</version>
+        <version>0.1.1-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-util</artifactId>
diff --git a/util/src/main/java/com/ning/billing/util/customfield/CustomizableEntityBase.java b/util/src/main/java/com/ning/billing/util/customfield/CustomizableEntityBase.java
index 05d0137..ac184be 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/CustomizableEntityBase.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/CustomizableEntityBase.java
@@ -23,18 +23,18 @@ import com.ning.billing.util.entity.EntityBase;
 public abstract class CustomizableEntityBase extends EntityBase implements CustomizableEntity {
     protected final FieldStore fields;
 
-    public CustomizableEntityBase(UUID id) {
+    public CustomizableEntityBase(final UUID id) {
         super(id);
         fields = DefaultFieldStore.create(getId(), getObjectName());
     }
 
     @Override
-    public String getFieldValue(String fieldName) {
+    public String getFieldValue(final String fieldName) {
         return fields.getValue(fieldName);
     }
 
     @Override
-    public void setFieldValue(String fieldName, String fieldValue) {
+    public void setFieldValue(final String fieldName, final String fieldValue) {
         fields.setValue(fieldName, fieldValue);
     }
 
@@ -44,6 +44,13 @@ public abstract class CustomizableEntityBase extends EntityBase implements Custo
     }
 
     @Override
+    public void addFields(final List<CustomField> fields) {
+        if (fields != null) {
+            this.fields.add(fields);
+        }
+    }
+
+    @Override
     public void clearFields() {
         fields.clear();
     }
diff --git a/util/src/main/java/com/ning/billing/util/entity/EntityCollectionDao.java b/util/src/main/java/com/ning/billing/util/entity/EntityCollectionDao.java
index 8a7a8c5..8134203 100644
--- a/util/src/main/java/com/ning/billing/util/entity/EntityCollectionDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/EntityCollectionDao.java
@@ -31,6 +31,10 @@ public interface EntityCollectionDao<T extends Entity> {
                      @Bind("objectType") final String objectType,
                      @BindBean final List<T> entities);
 
+    @SqlUpdate
+    public void clear(@Bind("objectId") final String objectId,
+                      @Bind("objectType") final String objectType);
+
     @SqlQuery
     public List<T> load(@Bind("objectId") final String objectId,
                         @Bind("objectType") final String objectType);
diff --git a/util/src/main/java/com/ning/billing/util/entity/EntityDao.java b/util/src/main/java/com/ning/billing/util/entity/EntityDao.java
index 1ee4fc1..3b5dd46 100644
--- a/util/src/main/java/com/ning/billing/util/entity/EntityDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/EntityDao.java
@@ -26,7 +26,10 @@ import java.util.List;
 
 public interface EntityDao<T extends Entity> {
     @SqlUpdate
-    public void save(@BindBean T entity);
+    public void create(@BindBean T entity);
+
+    @SqlUpdate
+    public void update(@BindBean T entity);
 
     @SqlQuery
     public T getById(@Bind("id") final String id);
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagDescriptionDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagDescriptionDao.java
index 4938d49..6bc029f 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagDescriptionDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagDescriptionDao.java
@@ -43,7 +43,11 @@ import org.skife.jdbi.v2.tweak.ResultSetMapper;
 public interface TagDescriptionDao extends EntityDao<TagDescription> {
     @Override
     @SqlUpdate
-    public void save(@TagDescriptionBinder TagDescription entity);
+    public void create(@TagDescriptionBinder TagDescription entity);
+
+    @Override
+    @SqlUpdate
+    public void update(@TagDescriptionBinder TagDescription entity);
 
     public class TagDescriptionMapper implements ResultSetMapper<TagDescription> {
         @Override
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 6a3062e..5d49fb2 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
@@ -20,12 +20,12 @@ import java.util.UUID;
 import com.ning.billing.util.entity.EntityCollectionBase;
 
 public class DefaultTagStore extends EntityCollectionBase<Tag> implements TagStore {
-    public DefaultTagStore(UUID objectId, String objectType) {
+    public DefaultTagStore(final UUID objectId, final String objectType) {
         super(objectId, objectType);
     }
 
     @Override
-    public String getEntityKey(Tag entity) {
+    public String getEntityKey(final Tag entity) {
         return entity.getName();
     }
 
@@ -58,14 +58,14 @@ public class DefaultTagStore extends EntityCollectionBase<Tag> implements TagSto
     }
 
     @Override
-    public void remove(String tagName) {
+    public void remove(final String tagName) {
         entities.remove(entities.get(tagName));
     }
 
     @Override
-    public boolean containsTag(String tagName) {
+    public boolean containsTag(final String tagName) {
         for (Tag tag : entities.values()) {
-            if (tag.getName() == tagName) {
+            if (tag.getName().equals(tagName)) {
                 return true;
             }
         }
diff --git a/util/src/main/resources/com/ning/billing/util/customfield/dao/FieldStoreDao.sql.stg b/util/src/main/resources/com/ning/billing/util/customfield/dao/FieldStoreDao.sql.stg
index c995694..57163a9 100644
--- a/util/src/main/resources/com/ning/billing/util/customfield/dao/FieldStoreDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/customfield/dao/FieldStoreDao.sql.stg
@@ -13,6 +13,11 @@ load() ::= <<
     WHERE object_id = :objectId AND object_type = :objectType;
 >>
 
+clear() ::= <<
+  DELETE FROM custom_fields
+  WHERE object_id = :objectId AND object_type = :objectType;
+>>
+
 test() ::= <<
   SELECT 1 FROM custom_fields;
 >>
diff --git a/util/src/main/resources/com/ning/billing/util/ddl.sql b/util/src/main/resources/com/ning/billing/util/ddl.sql
new file mode 100644
index 0000000..c68b18b
--- /dev/null
+++ b/util/src/main/resources/com/ning/billing/util/ddl.sql
@@ -0,0 +1,37 @@
+DROP TABLE IF EXISTS custom_fields;
+CREATE TABLE custom_fields (
+  id char(36) NOT NULL,
+  object_id char(36) NOT NULL,
+  object_type varchar(30) NOT NULL,
+  field_name varchar(30) NOT NULL,
+  field_value varchar(255) NOT NULL,
+  PRIMARY KEY(id)
+) ENGINE=innodb;
+CREATE INDEX custom_fields_object_id_object_type ON custom_fields(object_id, object_type);
+CREATE UNIQUE INDEX custom_fields_unique ON custom_fields(object_id, object_type, field_name);
+
+DROP TABLE IF EXISTS tag_descriptions;
+CREATE TABLE tag_descriptions (
+  id char(36) NOT NULL,
+  name varchar(20) NOT NULL,
+  created_by varchar(50) NOT NULL,
+  creation_date datetime NOT NULL,
+  description varchar(200) NOT NULL,
+  generate_invoice boolean DEFAULT false,
+  process_payment boolean DEFAULT false,
+  PRIMARY KEY(id)
+) ENGINE=innodb;
+CREATE UNIQUE INDEX tag_descriptions_name ON tag_descriptions(name);
+
+DROP TABLE IF EXISTS tags;
+CREATE TABLE tags (
+  id char(36) NOT NULL,
+  tag_description_id char(36) NOT NULL,
+  object_id char(36) NOT NULL,
+  object_type varchar(30) NOT NULL,
+  date_added datetime NOT NULL,
+  added_by varchar(50) NOT NULL,
+  PRIMARY KEY(id)
+) ENGINE = innodb;
+CREATE INDEX tags_by_object ON tags(object_id);
+CREATE UNIQUE INDEX tags_unique ON tags(tag_description_id, object_id);
\ No newline at end of file
diff --git a/util/src/main/resources/com/ning/billing/util/tag/dao/TagDescriptionDao.sql.stg b/util/src/main/resources/com/ning/billing/util/tag/dao/TagDescriptionDao.sql.stg
index bdde261..c71d063 100644
--- a/util/src/main/resources/com/ning/billing/util/tag/dao/TagDescriptionDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/tag/dao/TagDescriptionDao.sql.stg
@@ -1,12 +1,15 @@
 group TagDescriptionDao;
 
-save() ::= <<
+create() ::= <<
   INSERT INTO tag_descriptions(id, name, created_by, creation_date, description, generate_invoice, process_payment)
-  VALUES(:id, :name, :createdBy, :creationDate, :description, :generateInvoice, :processPayment)
-  ON DUPLICATE KEY UPDATE
-    name = :name, created_by = :createdBy, creation_date = :creationDate,
-    description := description, generate_invoice = :generateInvoice,
-    process_payment = :processPayment
+  VALUES(:id, :name, :createdBy, :creationDate, :description, :generateInvoice, :processPayment);
+>>
+
+update() ::= <<
+  UPDATE tag_descriptions
+  SET name = :name, created_by = :createdBy, creation_date = :creationDate,
+      description = :description, generate_invoice = :generateInvoice, process_payment = :processPayment)
+  WHERE id = :id;
 >>
 
 load() ::= <<
diff --git a/util/src/main/resources/com/ning/billing/util/tag/dao/TagStoreDao.sql.stg b/util/src/main/resources/com/ning/billing/util/tag/dao/TagStoreDao.sql.stg
index 3b53b2e..e72555d 100644
--- a/util/src/main/resources/com/ning/billing/util/tag/dao/TagStoreDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/tag/dao/TagStoreDao.sql.stg
@@ -17,6 +17,11 @@ load() ::= <<
     WHERE t.object_id = :objectId AND t.object_type = :objectType;
 >>
 
+clear() ::= <<
+  DELETE FROM tags
+  WHERE object_id = :objectId AND object_type = :objectType;
+>>
+
 test() ::= <<
   SELECT 1 FROM tags;
 >>