killbill-memoizeit

Changes

invoice/pom.xml 36(+36 -0)

invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceDao.sql.stg 24(+0 -24)

Details

diff --git a/account/src/main/java/com/ning/billing/account/api/Account.java b/account/src/main/java/com/ning/billing/account/api/Account.java
index 66f8c5d..7f48c7d 100644
--- a/account/src/main/java/com/ning/billing/account/api/Account.java
+++ b/account/src/main/java/com/ning/billing/account/api/Account.java
@@ -22,12 +22,10 @@ import com.ning.billing.catalog.api.Currency;
 
 import java.util.UUID;
 
-public class Account implements IAccount {
-    private final FieldStore fields;
+public class Account extends CustomizableEntityBase implements IAccount {
     private static IAccountDao dao;
 
-    private final UUID id;
-    private String key;
+    private String externalKey;
     private String email;
     private String name;
     private String phone;
@@ -39,23 +37,32 @@ public class Account implements IAccount {
     }
 
     public Account(UUID id) {
-        this.id = id;
-        fields = FieldStore.create(getId(), getObjectName());
+        super(id);
         dao = InjectorMagic.getAccountDao();
     }
 
-   @Override
-    public UUID getId() {
-        return id;
+    public Account(IAccountData data) {
+        this();
+        this.externalKey = data.getExternalKey();
+        this.email = data.getEmail();
+        this.name = data.getName();
+        this.phone = data.getPhone();
+        this.currency = data.getCurrency();
+        this.billCycleDay = data.getBillCycleDay();
     }
 
     @Override
-    public String getKey() {
-        return key;
+    public String getObjectName() {
+        return "Account";
+    }
+
+    @Override
+    public String getExternalKey() {
+        return externalKey;
     }
 
-    public Account withKey(String key) {
-        this.key = key;
+    public Account externalKey(String externalKey) {
+        this.externalKey = externalKey;
         return this;
     }
 
@@ -64,7 +71,7 @@ public class Account implements IAccount {
         return name;
     }
 
-    public Account withName(String name) {
+    public Account name(String name) {
         this.name = name;
         return this;
     }
@@ -74,7 +81,7 @@ public class Account implements IAccount {
         return email;
     }
 
-    public Account withEmail(String email) {
+    public Account email(String email) {
         this.email = email;
         return this;
     }
@@ -84,7 +91,7 @@ public class Account implements IAccount {
         return phone;
     }
 
-    public Account withPhone(String phone) {
+    public Account phone(String phone) {
         this.phone = phone;
         return this;
     }
@@ -94,7 +101,7 @@ public class Account implements IAccount {
         return billCycleDay;
     }
 
-    public Account withBillCycleDay(int billCycleDay) {
+    public Account billCycleDay(int billCycleDay) {
         this.billCycleDay = billCycleDay;
         return this;
     }
@@ -104,7 +111,7 @@ public class Account implements IAccount {
         return currency;
     }
 
-    public Account withCurrency(Currency currency) {
+    public Account currency(Currency currency) {
         this.currency = currency;
         return this;
     }
@@ -134,46 +141,19 @@ public class Account implements IAccount {
     }
 
     @Override
-    public String getFieldValue(String fieldName) {
-        return fields.getValue(fieldName);
+    protected void saveObject() {
+        dao.saveAccount(this);
     }
 
     @Override
-    public void setFieldValue(String fieldName, String fieldValue) {
-        fields.setValue(fieldName, fieldValue);
+    protected void updateObject() {
+        dao.updateAccount(this);
     }
 
     @Override
-    public void save() {
-        saveObject();
-        saveCustomFields();
-    }
-
-    @Override
-    public void load() {
-        loadObject();
-        loadCustomFields();
-    }
-
-    private void saveCustomFields() {
-        fields.save();
-    }
-
-    protected void loadCustomFields() {
-        fields.load();
-    }
-
-    public String getObjectName() {
-        return "Account";
-    }
-
-    private void saveObject() {
-        dao.save(this);
-    }
-
-    private void loadObject() {
+    protected void loadObject() {
         IAccount that = dao.getAccountById(id);
-        this.key = that.getKey();
+        this.externalKey = that.getExternalKey();
         this.email = that.getEmail();
         this.name = that.getName();
         this.phone = that.getPhone();
diff --git a/account/src/main/java/com/ning/billing/account/api/AccountUserApi.java b/account/src/main/java/com/ning/billing/account/api/AccountUserApi.java
index d236c5f..9b79098 100644
--- a/account/src/main/java/com/ning/billing/account/api/AccountUserApi.java
+++ b/account/src/main/java/com/ning/billing/account/api/AccountUserApi.java
@@ -42,7 +42,7 @@ public class AccountUserApi implements IAccountUserApi {
     }
 
     @Override
-    public IAccount getAccountFromId(UUID uid) {
+    public IAccount getAccountById(UUID uid) {
         return dao.getAccountById(uid);
     }
 
diff --git a/account/src/main/java/com/ning/billing/account/api/CustomizableEntityBase.java b/account/src/main/java/com/ning/billing/account/api/CustomizableEntityBase.java
new file mode 100644
index 0000000..516d58c
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/api/CustomizableEntityBase.java
@@ -0,0 +1,60 @@
+/*
+ * 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 java.util.UUID;
+
+public abstract class CustomizableEntityBase extends EntityBase implements ICustomizableEntity {
+    protected final FieldStore fields;
+
+    public CustomizableEntityBase(UUID id) {
+        super(id);
+        fields = FieldStore.create(getId(), getObjectName());
+    }
+
+    @Override
+    public String getFieldValue(String fieldName) {
+        return fields.getValue(fieldName);
+    }
+
+    @Override
+    public void setFieldValue(String fieldName, String fieldValue) {
+        fields.setValue(fieldName, fieldValue);
+    }
+
+    @Override
+    public void save() {
+        saveObject();
+        fields.save();
+    }
+
+    @Override
+    public void load() {
+        loadObject();
+        loadCustomFields();
+    }
+
+    protected void loadCustomFields() {
+        fields.load();
+    }
+
+    public abstract String getObjectName();
+
+    protected abstract void saveObject();
+
+    protected abstract void loadObject();
+}
diff --git a/account/src/main/java/com/ning/billing/account/api/EntityBase.java b/account/src/main/java/com/ning/billing/account/api/EntityBase.java
new file mode 100644
index 0000000..710bd13
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/api/EntityBase.java
@@ -0,0 +1,69 @@
+/*
+ * 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 java.util.UUID;
+
+public abstract class EntityBase implements IEntity, IPersistable {
+    protected final UUID id;
+    protected boolean isNew;
+
+    public EntityBase(UUID id) {
+        this.id = id;
+        this.isNew = false;
+    }
+
+    public EntityBase() {
+        this(UUID.randomUUID());
+        this.isNew = true;
+    }
+
+    @Override
+    public UUID getId() {
+        return id;
+    }
+
+    @Override
+    public String getIdAsString() {
+        return id.toString();
+    }
+
+    @Override
+    public boolean isNew() {
+        return this.isNew;
+    }
+
+    @Override
+    public void setAsSaved() {
+        this.isNew = false;
+    }
+
+    @Override
+    public void save() {
+        if (isNew) {
+            saveObject();
+        } else {
+            updateObject();
+        }
+    }
+
+    protected abstract void saveObject();
+    protected abstract void updateObject();
+
+    @Override
+    public abstract void load();
+}
diff --git a/account/src/main/java/com/ning/billing/account/api/EntityCollectionBase.java b/account/src/main/java/com/ning/billing/account/api/EntityCollectionBase.java
new file mode 100644
index 0000000..f5a834b
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/api/EntityCollectionBase.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.account.api;
+
+import com.ning.billing.account.dao.IEntityCollectionDao;
+
+import java.util.*;
+
+public abstract class EntityCollectionBase<T extends IEntity> {
+    protected Map<String, T> entities = new HashMap<String, T>();
+    protected final UUID objectId;
+    protected final String objectType;
+
+    public EntityCollectionBase(UUID objectId, String objectType) {
+        this.objectId = objectId;
+        this.objectType = objectType;
+    }
+
+    public List<T> getNewEntities() {
+        List<T> newEntities = new ArrayList<T>();
+        for (T entity : entities.values()) {
+            if (entity.isNew()) {
+                newEntities.add(entity);
+            }
+        }
+
+        return newEntities;
+    }
+
+    public List<T> getUpdatedEntities() {
+        List<T> updatedEntities = new ArrayList<T>();
+        for (T entity : entities.values()) {
+            if (!entity.isNew()) {
+                updatedEntities.add(entity);
+            }
+        }
+
+        return updatedEntities;
+    }
+
+    public void save() {
+        IEntityCollectionDao<T> dao = getCollectionDao();
+
+        dao.create(objectId.toString(), objectType, getNewEntities());
+        setEntitiesAsSaved();
+
+        dao.update(objectId.toString(), objectType, getUpdatedEntities());
+    }
+
+    private void setEntitiesAsSaved() {
+        for (T entity : entities.values()) {
+            entity.setAsSaved();
+        }
+    }
+
+    public void load() {
+        IEntityCollectionDao<T> dao = getCollectionDao();
+
+        List<T> entities = dao.load(objectId.toString(), objectType);
+        this.entities.clear();
+        if (entities != null) {
+            for (T entity : entities) {
+                this.entities.put(getEntityKey(entity), entity);
+            }
+        }
+    }
+
+    protected abstract String getEntityKey(T entity);
+    protected abstract IEntityCollectionDao<T> getCollectionDao();
+}
diff --git a/account/src/main/java/com/ning/billing/account/api/FieldStore.java b/account/src/main/java/com/ning/billing/account/api/FieldStore.java
index 2d1da7f..0a58da4 100644
--- a/account/src/main/java/com/ning/billing/account/api/FieldStore.java
+++ b/account/src/main/java/com/ning/billing/account/api/FieldStore.java
@@ -16,85 +16,130 @@
 
 package com.ning.billing.account.api;
 
-import com.ning.billing.account.dao.IFieldStoreDao;
+import com.ning.billing.account.dao.IEntityCollectionDao;
 import com.ning.billing.account.glue.InjectorMagic;
 
-import java.util.*;
-
-public class FieldStore implements IFieldStore {
-    private Map<String, ICustomField> fields = new HashMap<String, ICustomField>();
-    private final UUID objectId;
-    private final String objectType;
+import java.util.UUID;
 
+public class FieldStore extends EntityCollectionBase<ICustomField> {
     public FieldStore(UUID objectId, String objectType) {
-        this.objectId = objectId;
-        this.objectType = objectType;
+        super(objectId, objectType);
     }
 
     public static FieldStore create(UUID objectId, String objectType) {
         return new FieldStore(objectId, objectType);
     }
 
-    public void setValue(String fieldName, String fieldValue) {
-        if (fields.containsKey(fieldName)) {
-            fields.get(fieldName).setValue(fieldValue);
-        } else {
-            fields.put(fieldName, new CustomField(fieldName, fieldValue));
-        }
-    }
-
-    public String getValue(String fieldName) {
-        if (fields.containsKey(fieldName)) {
-            return fields.get(fieldName).getValue();
-        } else {
-            return null;
-        }
-    }
-
     @Override
-    public List<ICustomField> getNewFields() {
-        List<ICustomField> newFields = new ArrayList<ICustomField>();
-        for (ICustomField field : fields.values()) {
-            if (field.isNew()) {
-                newFields.add(field);
-            }
-        }
-
-        return newFields;
+    protected String getEntityKey(ICustomField entity) {
+        return entity.getName();
     }
 
     @Override
-    public List<ICustomField> getUpdatedFields() {
-        List<ICustomField> updatedFields = new ArrayList<ICustomField>();
-        for (ICustomField field : fields.values()) {
-            if (!field.isNew()) {
-                updatedFields.add(field);
-            }
-        }
-
-        return updatedFields;
+    protected IEntityCollectionDao<ICustomField> getCollectionDao() {
+        return InjectorMagic.getFieldStoreDao();
     }
 
-    public void save() {
-        IFieldStoreDao dao = InjectorMagic.getFieldStoreDao();
-
-        List<ICustomField> newFields = getNewFields();
-        dao.createFields(objectId.toString(), objectType, newFields);
-        for (ICustomField field : newFields) {
-            field.setAsSaved();
+    public void setValue(String fieldName, String fieldValue) {
+        if (entities.containsKey(fieldName)) {
+            entities.get(fieldName).setValue(fieldValue);
+        } else {
+            entities.put(fieldName, new CustomField(fieldName, fieldValue));
         }
-
-        dao.saveFields(objectId.toString(), objectType, getUpdatedFields());
     }
 
-    public void load() {
-        IFieldStoreDao dao = InjectorMagic.getFieldStoreDao();
-        List<ICustomField> fields = dao.getFields(objectId.toString(), objectType);
-        this.fields.clear();
-        if (fields != null) {
-            for (ICustomField field : fields) {
-                this.fields.put(field.getName(), field);
-            }
+    public String getValue(String fieldName) {
+        if (entities.containsKey(fieldName)) {
+            return entities.get(fieldName).getValue();
+        } else {
+            return null;
         }
     }
-}
\ No newline at end of file
+}
+//import com.ning.billing.account.dao.IFieldStoreDao;
+//import com.ning.billing.account.glue.InjectorMagic;
+//
+//import java.util.*;
+//
+//public class FieldStore implements IFieldStore {
+//    private Map<String, ICustomField> fields = new HashMap<String, ICustomField>();
+//    private final UUID objectId;
+//    private final String objectType;
+//
+//    public FieldStore(UUID objectId, String objectType) {
+//        this.objectId = objectId;
+//        this.objectType = objectType;
+//    }
+//
+//    public static FieldStore create(UUID objectId, String objectType) {
+//        return new FieldStore(objectId, objectType);
+//    }
+//
+//    public void setValue(String fieldName, String fieldValue) {
+//        if (fields.containsKey(fieldName)) {
+//            fields.get(fieldName).setValue(fieldValue);
+//        } else {
+//            fields.put(fieldName, new CustomField(fieldName, fieldValue));
+//        }
+//    }
+//
+//    public String getValue(String fieldName) {
+//        if (fields.containsKey(fieldName)) {
+//            return fields.get(fieldName).getValue();
+//        } else {
+//            return null;
+//        }
+//    }
+//
+//    @Override
+//    public List<ICustomField> getNewFields() {
+//        List<ICustomField> newFields = new ArrayList<ICustomField>();
+//        for (ICustomField field : fields.values()) {
+//            if (field.isNew()) {
+//                newFields.add(field);
+//            }
+//        }
+//
+//        return newFields;
+//    }
+//
+//    @Override
+//    public List<ICustomField> getUpdatedFields() {
+//        List<ICustomField> updatedFields = new ArrayList<ICustomField>();
+//        for (ICustomField field : fields.values()) {
+//            if (!field.isNew()) {
+//                updatedFields.add(field);
+//            }
+//        }
+//
+//        return updatedFields;
+//    }
+//
+//    public void save() {
+//        IFieldStoreDao dao = InjectorMagic.getFieldStoreDao();
+//
+//        List<ICustomField> newFields = getNewFields();
+//        dao.createFields(objectId.toString(), objectType, newFields);
+//        setEntitiesAsSaved();
+//
+//        dao.saveFields(objectId.toString(), objectType, getUpdatedFields());
+//    }
+//
+//    private void setEntitiesAsSaved() {
+//        for (ICustomField field : fields.values()) {
+//            field.setAsSaved();
+//        }
+//    }
+//
+//    public void load() {
+//        IFieldStoreDao dao = InjectorMagic.getFieldStoreDao();
+//
+//        List<ICustomField> fields = dao.getFields(objectId.toString(), objectType);
+//        this.fields.clear();
+//        if (fields != null) {
+//            for (ICustomField field : fields) {
+//                this.fields.put(field.getName(), field);
+//            }
+//        }
+//    }
+//}
\ No newline at end of file
diff --git a/account/src/main/java/com/ning/billing/account/api/ICustomField.java b/account/src/main/java/com/ning/billing/account/api/ICustomField.java
index fa13e42..fe1ac70 100644
--- a/account/src/main/java/com/ning/billing/account/api/ICustomField.java
+++ b/account/src/main/java/com/ning/billing/account/api/ICustomField.java
@@ -16,18 +16,10 @@
 
 package com.ning.billing.account.api;
 
-import java.util.UUID;
-
-public interface ICustomField {
-    public UUID getId();
-
+public interface ICustomField extends IEntity {
     public String getName();
 
     public String getValue();
 
     public void setValue(String value);
-
-    public boolean isNew();
-
-    public void setAsSaved();
 }
diff --git a/account/src/main/java/com/ning/billing/account/api/Tag.java b/account/src/main/java/com/ning/billing/account/api/Tag.java
new file mode 100644
index 0000000..40c26fd
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/api/Tag.java
@@ -0,0 +1,75 @@
+/*
+ * 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.account.dao.ITagDao;
+
+import java.util.UUID;
+
+public class Tag extends EntityBase {
+    private ITagDao dao;
+
+    private UUID tagDescriptionId;
+    private String description;
+    private UUID objectId;
+    private String objectType;
+
+    public Tag() {
+        super();
+    }
+
+    public Tag(UUID id) {
+        super(id);
+    }
+
+    public UUID getTagDescriptionId() {
+        return tagDescriptionId;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public UUID getObjectId() {
+        return objectId;
+    }
+
+    public String getObjectType() {
+        return objectType;
+    }
+
+    @Override
+    protected void saveObject() {
+        dao.create(this);
+    }
+
+    @Override
+    protected void updateObject() {
+        dao.update(this);
+    }
+
+    @Override
+    public void load() {
+        Tag that = dao.load(id);
+        if (that != null) {
+            this.tagDescriptionId = that.tagDescriptionId;
+            this.description = that.description;
+            this.objectId = that.objectId;
+            this.objectType = that.objectType;
+        }
+    }
+}
diff --git a/account/src/main/java/com/ning/billing/account/api/TagDescription.java b/account/src/main/java/com/ning/billing/account/api/TagDescription.java
new file mode 100644
index 0000000..5618ca8
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/api/TagDescription.java
@@ -0,0 +1,116 @@
+/*
+ * 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.account.dao.ITagDescriptionDao;
+import org.joda.time.DateTime;
+
+import java.util.UUID;
+
+public class TagDescription extends EntityBase {
+    private ITagDescriptionDao dao;
+
+    private String name;
+    private String addedBy;
+    private DateTime created;
+    private String description;
+    private boolean generateInvoice;
+    private boolean processPayment;
+
+    public TagDescription() {
+        super();
+    }
+
+    public TagDescription(UUID id) {
+        super(id);
+    }
+    
+    public String getName() {
+        return name;
+    }
+
+    public TagDescription withName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    public String getAddedBy() {
+        return addedBy;
+    }
+
+    public TagDescription withAddedBy(String addedBy) {
+        this.addedBy = addedBy;
+        return this;
+    }
+
+    public DateTime getCreated() {
+        return created;
+    }
+
+    public TagDescription withCreated(DateTime created) {
+        this.created = created;
+        return this;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public TagDescription withDescription(String description) {
+        this.description = description;
+        return this;
+    }
+
+    public boolean getGenerateInvoice() {
+        return generateInvoice;
+    }
+
+    public TagDescription withGenerateInvoice(boolean generateInvoice) {
+        this.generateInvoice = generateInvoice;
+        return this;
+    }
+
+    public boolean getProcessPayment() {
+        return processPayment;
+    }
+
+    public TagDescription withProcessPayment(boolean processPayment) {
+        this.processPayment = processPayment;
+        return this;
+    }
+
+    @Override
+    protected void saveObject() {
+        dao.create(this);
+    }
+
+    @Override
+    protected void updateObject() {
+        dao.update(this);
+    }
+
+    @Override
+    public void load() {
+        TagDescription that = dao.load(id.toString());
+        this.name = that.name;
+        this.addedBy = that.addedBy;
+        this.created = that.created;
+        this.description = that.description;
+        this.generateInvoice = that.generateInvoice;
+        this.processPayment = that.processPayment;
+    }
+}
diff --git a/account/src/main/java/com/ning/billing/account/api/TagStore.java b/account/src/main/java/com/ning/billing/account/api/TagStore.java
new file mode 100644
index 0000000..d9456bd
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/api/TagStore.java
@@ -0,0 +1,38 @@
+/*
+ * 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.account.dao.IEntityCollectionDao;
+import com.ning.billing.account.glue.InjectorMagic;
+
+import java.util.UUID;
+
+public class TagStore extends EntityCollectionBase<Tag> {
+    public TagStore(UUID objectId, String objectType) {
+        super(objectId, objectType);
+    }
+
+    @Override
+    protected String getEntityKey(Tag entity) {
+        return entity.getDescription();
+    }
+
+    @Override
+    protected IEntityCollectionDao<Tag> getCollectionDao() {
+        return InjectorMagic.getTagStoreDao();
+    }
+}
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountDao.java
index 15bf60f..6f5c5fb 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountDao.java
@@ -26,7 +26,6 @@ import java.util.List;
 import java.util.UUID;
 
 public class AccountDao implements IAccountDao {
-
     private final IAccountDaoSql dao;
 
     @Inject
@@ -36,7 +35,7 @@ public class AccountDao implements IAccountDao {
 
     @Override
     public IAccount createAccount(IAccountData input) {
-        IAccount result = new Account().withKey(input.getKey());
+        IAccount result = new Account(input);
         dao.insertAccount(result);
         return result;
     }
@@ -48,7 +47,7 @@ public class AccountDao implements IAccountDao {
 
     @Override
     public IAccount getAccountById(UUID uid) {
-        return dao.getAccountFromId(uid.toString());
+        return dao.getAccountById(uid.toString());
     }
 
     @Override
@@ -62,7 +61,12 @@ public class AccountDao implements IAccountDao {
     }
 
     @Override
-    public void save(IAccount account) {
+    public void saveAccount(IAccount account) {
         dao.insertAccount(account);
     }
+
+    @Override
+    public void updateAccount(IAccount account) {
+        dao.updateAccount(account);
+    }
 }
diff --git a/account/src/main/java/com/ning/billing/account/dao/CustomFieldMapper.java b/account/src/main/java/com/ning/billing/account/dao/CustomFieldMapper.java
new file mode 100644
index 0000000..92f2c07
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/dao/CustomFieldMapper.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.account.dao;
+
+import com.ning.billing.account.api.CustomField;
+import com.ning.billing.account.api.ICustomField;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.tweak.ResultSetMapper;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.UUID;
+
+public class CustomFieldMapper implements ResultSetMapper<ICustomField> {
+        @Override
+        public ICustomField map(int index, ResultSet result, StatementContext context) throws SQLException {
+            UUID id = UUID.fromString(result.getString("id"));
+            String fieldName = result.getString("field_name");
+            String fieldValue = result.getString("field_value");
+            return new CustomField(id, fieldName, fieldValue);
+        }
+}
diff --git a/account/src/main/java/com/ning/billing/account/dao/FieldStoreDao.java b/account/src/main/java/com/ning/billing/account/dao/FieldStoreDao.java
index e608f5e..baa4a99 100644
--- a/account/src/main/java/com/ning/billing/account/dao/FieldStoreDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/FieldStoreDao.java
@@ -31,18 +31,18 @@ public class FieldStoreDao implements IFieldStoreDao {
     }
 
     @Override
-    public void saveFields(String objectId, String objectType, List<ICustomField> fields) {
-        dao.saveFields(objectId, objectType, fields);
+    public void update(String objectId, String objectType, List<ICustomField> fields) {
+        dao.update(objectId, objectType, fields);
     }
 
     @Override
-    public void createFields(String objectId, String objectType,  List<ICustomField> fields) {
-        dao.createFields(objectId, objectType, fields);
+    public void create(String objectId, String objectType,  List<ICustomField> fields) {
+        dao.create(objectId, objectType, fields);
     }
 
     @Override
-    public List<ICustomField> getFields(String objectId, String objectType) {
-        return dao.getFields(objectId, objectType);
+    public List<ICustomField> load(String objectId, String objectType) {
+        return dao.load(objectId, objectType);
     }
 
     @Override
diff --git a/account/src/main/java/com/ning/billing/account/dao/IAccountDao.java b/account/src/main/java/com/ning/billing/account/dao/IAccountDao.java
index e23fbfa..814a81a 100644
--- a/account/src/main/java/com/ning/billing/account/dao/IAccountDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/IAccountDao.java
@@ -23,7 +23,6 @@ import java.util.List;
 import java.util.UUID;
 
 public interface IAccountDao {
-
     public IAccount createAccount(IAccountData account);
 
     public IAccount getAccountById(UUID uid);
@@ -34,5 +33,7 @@ public interface IAccountDao {
 
     public void test();
 
-    public void save(IAccount account);
+    public void saveAccount(IAccount account);
+
+    public void updateAccount(IAccount account);
 }
diff --git a/account/src/main/java/com/ning/billing/account/dao/IAccountDaoSql.java b/account/src/main/java/com/ning/billing/account/dao/IAccountDaoSql.java
index 7cb9eda..63b3444 100644
--- a/account/src/main/java/com/ning/billing/account/dao/IAccountDaoSql.java
+++ b/account/src/main/java/com/ning/billing/account/dao/IAccountDaoSql.java
@@ -18,18 +18,17 @@ package com.ning.billing.account.dao;
 
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.IAccount;
+import com.ning.billing.catalog.api.Currency;
 import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.StatementContext;
-import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.Binder;
-import org.skife.jdbi.v2.sqlobject.SqlQuery;
-import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.*;
 import org.skife.jdbi.v2.sqlobject.customizers.Mapper;
 import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
 import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
+import java.lang.annotation.*;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.List;
@@ -37,42 +36,58 @@ import java.util.UUID;
 
 @ExternalizedSqlViaStringTemplate3()
 public interface IAccountDaoSql extends Transactional<IAccountDaoSql>, CloseMe {
+    @SqlUpdate
+    public void insertAccount(@AccountBinder IAccount account);
 
     @SqlUpdate
-    public void insertAccount(@Bind(binder = IAccountSqlBinder.class) IAccount account);
+    public void updateAccount(@AccountBinder IAccount account);
 
     @SqlQuery
-    @Mapper(IAccountSqlMapper.class)
-    public IAccount getAccountByKey(@Bind("key_name") String key);
+    @Mapper(AccountMapper.class)
+    public IAccount getAccountByKey(@Bind("externalKey") String externalKey);
 
     @SqlQuery
-    @Mapper(IAccountSqlMapper.class)
-    public IAccount getAccountFromId(@Bind("id") String id);
+    @Mapper(AccountMapper.class)
+    public IAccount getAccountById(@Bind("id") String id);
 
     @SqlQuery
-    @Mapper(IAccountSqlMapper.class)
+    @Mapper(AccountMapper.class)
     public List<IAccount> getAccounts();
 
     @SqlUpdate
     public void test();
 
-    public static class IAccountSqlBinder implements Binder<Bind, IAccount> {
-
+    public static class AccountMapper implements ResultSetMapper<IAccount> {
         @Override
-        public void bind(SQLStatement stmt, Bind bind, IAccount account) {
-            stmt.bind("id", account.getId().toString());
-            stmt.bind("key_name", account.getKey());
+        public IAccount map(int index, ResultSet result, StatementContext context) throws SQLException {
+            UUID id = UUID.fromString(result.getString("id"));
+            String externalKey = result.getString("external_key");
+            String email = result.getString("email");
+            String name = result.getString("name");
+            String phone = result.getString("phone");
+            Currency currency = Currency.valueOf(result.getString("currency"));
+
+            return new Account(id).externalKey(externalKey).email(email).name(name).phone(phone).currency(currency);
         }
     }
 
-    public static class IAccountSqlMapper implements ResultSetMapper<IAccount> {
-
-        @Override
-        public IAccount map(int index, ResultSet r, StatementContext ctx)
-                throws SQLException {
-            UUID id = UUID.fromString(r.getString("id"));
-            String key = r.getString("key_name");
-            return new Account(id).withKey(key);
+    @BindingAnnotation(AccountBinder.AccountBinderFactory.class)
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.PARAMETER})
+    public @interface AccountBinder {
+        public static class AccountBinderFactory implements BinderFactory {
+            public Binder build(Annotation annotation) {
+                return new Binder<AccountBinder, Account>() {
+                    public void bind(SQLStatement q, AccountBinder bind, Account account) {
+                        q.bind("id", account.getId().toString());
+                        q.bind("externalKey", account.getExternalKey());
+                        q.bind("email", account.getEmail());
+                        q.bind("name", account.getName());
+                        q.bind("phone", account.getPhone());
+                        q.bind("currency", account.getCurrency().toString());
+                    }
+                };
+            }
         }
     }
 }
diff --git a/account/src/main/java/com/ning/billing/account/dao/IEntityCollectionDao.java b/account/src/main/java/com/ning/billing/account/dao/IEntityCollectionDao.java
new file mode 100644
index 0000000..8a12109
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/dao/IEntityCollectionDao.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.account.dao;
+
+import com.ning.billing.account.api.IEntity;
+import org.skife.jdbi.v2.sqlobject.*;
+
+import java.util.List;
+
+public interface IEntityCollectionDao<T extends IEntity> {
+    @SqlBatch
+    public void update(@Bind("objectId") final String objectId,
+                       @Bind("objectType") final String objectType,
+                       @BindBean final List<T> entities);
+
+    @SqlBatch
+    public void create(@Bind("objectId") final String objectId,
+                       @Bind("objectType") final String objectType,
+                       @BindBean final List<T> entities);
+
+    @SqlQuery
+    public List<T> load(@Bind("objectId") final String objectId,
+                        @Bind("objectType") final String objectType);
+
+    @SqlUpdate
+    public void test();
+}
diff --git a/account/src/main/java/com/ning/billing/account/dao/IEntityDao.java b/account/src/main/java/com/ning/billing/account/dao/IEntityDao.java
new file mode 100644
index 0000000..f56a833
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/dao/IEntityDao.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.account.dao;
+
+import com.ning.billing.account.api.IEntity;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.BindBean;
+
+public interface IEntityDao<T extends IEntity> {
+    public void create(@BindBean final T entity);
+    public void update(@BindBean final T entity);
+    public T load(@Bind("id") final String id);
+}
diff --git a/account/src/main/java/com/ning/billing/account/dao/IFieldStoreDao.java b/account/src/main/java/com/ning/billing/account/dao/IFieldStoreDao.java
index 56d6c04..ed89b55 100644
--- a/account/src/main/java/com/ning/billing/account/dao/IFieldStoreDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/IFieldStoreDao.java
@@ -16,57 +16,11 @@
 
 package com.ning.billing.account.dao;
 
-import com.ning.billing.account.api.CustomField;
 import com.ning.billing.account.api.ICustomField;
-import org.skife.jdbi.v2.SQLStatement;
-import org.skife.jdbi.v2.StatementContext;
-import org.skife.jdbi.v2.sqlobject.*;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
-import org.skife.jdbi.v2.tweak.ResultSetMapper;
-
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.List;
-import java.util.UUID;
 
 @ExternalizedSqlViaStringTemplate3
-public interface IFieldStoreDao {
-    @SqlBatch
-    public void saveFields(@Bind("objectId") final String objectId,
-                           @Bind("objectType") final String objectType,
-                           @BindBean final List<ICustomField> fields);
-
-    @SqlBatch
-    public void createFields(@Bind("objectId") final String objectId,
-                            @Bind("objectType") final String objectType,
-                            @BindBean final List<ICustomField> fields);
-
-    @SqlQuery
-    @RegisterMapper(CustomFieldMapper.class)
-    public List<ICustomField> getFields(@Bind("objectId") final String objectId,
-                                        @Bind("objectType") final String objectType);
-
-    @SqlUpdate
-    public void test();
-
-    public static class CustomFieldBinder implements Binder<Bind, ICustomField> {
-        @Override
-        public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, ICustomField field) {
-            stmt.bind("id", field.getId().toString());
-            stmt.bind("fieldName", field.getName());
-            stmt.bind("fieldValue", field.getValue());
-        }
-    }
-
-    public static class CustomFieldMapper implements ResultSetMapper<ICustomField> {
-        @Override
-        public ICustomField map(int index, ResultSet result, StatementContext context)
-                throws SQLException {
-            UUID id = UUID.fromString(result.getString("id"));
-            String fieldName = result.getString("field_name");
-            String fieldValue = result.getString("field_value");
-            return new CustomField(id, fieldName, fieldValue);
-        }
-    }
+@RegisterMapper(CustomFieldMapper.class)
+public interface IFieldStoreDao extends IEntityCollectionDao<ICustomField> {
 }
diff --git a/account/src/main/java/com/ning/billing/account/dao/ITagDao.java b/account/src/main/java/com/ning/billing/account/dao/ITagDao.java
new file mode 100644
index 0000000..33d71ae
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/dao/ITagDao.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.account.dao;
+
+import com.ning.billing.account.api.Tag;
+
+import java.util.UUID;
+
+public interface ITagDao {
+    public void create(Tag tag);
+    public void update(Tag tag);
+    public Tag load(UUID id);
+
+}
diff --git a/account/src/main/java/com/ning/billing/account/dao/ITagDescriptionDao.java b/account/src/main/java/com/ning/billing/account/dao/ITagDescriptionDao.java
new file mode 100644
index 0000000..f1fe87f
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/dao/ITagDescriptionDao.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.account.dao;
+
+import com.ning.billing.account.api.TagDescription;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+
+@ExternalizedSqlViaStringTemplate3
+public interface ITagDescriptionDao extends IEntityDao<TagDescription> {
+}
\ No newline at end of file
diff --git a/account/src/main/java/com/ning/billing/account/dao/ITagStoreDao.java b/account/src/main/java/com/ning/billing/account/dao/ITagStoreDao.java
new file mode 100644
index 0000000..9092c29
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/dao/ITagStoreDao.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.account.dao;
+
+import com.ning.billing.account.api.Tag;
+import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+
+@ExternalizedSqlViaStringTemplate3
+@RegisterMapper(TagDescriptionMapper.class)
+public interface ITagStoreDao extends IEntityCollectionDao<Tag> {
+}
diff --git a/account/src/main/java/com/ning/billing/account/dao/TagDescriptionMapper.java b/account/src/main/java/com/ning/billing/account/dao/TagDescriptionMapper.java
new file mode 100644
index 0000000..483ba6a
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/dao/TagDescriptionMapper.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.account.dao;
+
+import com.ning.billing.account.api.TagDescription;
+import org.skife.jdbi.v2.BeanMapper;
+
+public class TagDescriptionMapper extends BeanMapper<TagDescription> {
+    public TagDescriptionMapper() {
+        super(TagDescription.class);
+    }
+//        @Override
+//        public TagDescription map(int index, ResultSet result, StatementContext context) throws SQLException {
+//            UUID id = UUID.fromString(result.getString("id"));
+//            String name = result.getString("tag_name");
+//            UUID createdById = UUID.fromString(result.getString("created_by_id"));
+//            DateTime created = new DateTime(result.getDate("created"));
+//            String description = result.getString("description");
+//            boolean generateInvoice = result.getBoolean("generate_invoice");
+//            boolean processPayment = result.getBoolean("process_payment");
+//
+//            return new TagDescription(id).withName(name)
+//                                         .withCreatedById(createdById)
+//                                         .withCreated(created)
+//                                         .withDescription(description)
+//                                         .withGenerateInvoice(generateInvoice)
+//                                         .withProcessPayment(processPayment);
+//        }
+}
diff --git a/account/src/main/java/com/ning/billing/account/glue/InjectorMagic.java b/account/src/main/java/com/ning/billing/account/glue/InjectorMagic.java
index 6e00bf8..d27a20b 100644
--- a/account/src/main/java/com/ning/billing/account/glue/InjectorMagic.java
+++ b/account/src/main/java/com/ning/billing/account/glue/InjectorMagic.java
@@ -20,6 +20,7 @@ import com.google.inject.Inject;
 import com.google.inject.Injector;
 import com.ning.billing.account.dao.IAccountDao;
 import com.ning.billing.account.dao.IFieldStoreDao;
+import com.ning.billing.account.dao.ITagStoreDao;
 
 public class InjectorMagic {
     public static InjectorMagic instance;
@@ -54,4 +55,8 @@ public class InjectorMagic {
     public static IAccountDao getAccountDao() {
         return InjectorMagic.get().getInstance(IAccountDao.class);
     }
+
+    public static ITagStoreDao getTagStoreDao() {
+        return InjectorMagic.get().getInstance(ITagStoreDao.class);
+    }
 }
diff --git a/account/src/main/resources/com/ning/billing/account/dao/IAccountDaoSql.sql.stg b/account/src/main/resources/com/ning/billing/account/dao/IAccountDaoSql.sql.stg
index c7fa4fa..b8faa2a 100644
--- a/account/src/main/resources/com/ning/billing/account/dao/IAccountDaoSql.sql.stg
+++ b/account/src/main/resources/com/ning/billing/account/dao/IAccountDaoSql.sql.stg
@@ -1,44 +1,39 @@
 group IAccountDaoSql;
 
 insertAccount() ::= <<
-    insert into accounts (
-      id
-      , key_name
-    ) values (
-      :id
-      , :key_name
-    );
->> 
+    insert into accounts (id, external_key, email, name, phone, currency)
+    values (:id, :externalKey, :email, :name, :phone, :currency);
+>>
+
+updateAccount() ::= <<
+    update accounts
+    set external_key = :externalKey, email = :email, name = :name, phone = :phone, currency = :currency
+    where id = :id;
+>>
 
-getAccountByKey(key) ::= <<
-    select
-      id
-      , key_name
+getAccountByKey() ::= <<
+    select id, external_key, email, name, phone, currency
     from accounts
     where
-      key_name = :key_name
+      external_key = :externalKey
     ;
 >>
 
-getAccountFromId(id) ::= <<
-    select
-      id
-      , key_name
+getAccountById() ::= <<
+    select id, external_key, email, name, phone, currency
     from accounts
     where
       id = :id
     ;
 >>
 
-test() ::= <<
-    select 1 from accounts;
->>
-
 getAccounts() ::= <<
-    select
-      id
-      , key_name
+    select id, external_key, email, name, phone, currency
     from accounts
     ;
 >>
+
+test() ::= <<
+    select 1 from accounts;
+>>
 ;
\ No newline at end of file
diff --git a/account/src/main/resources/com/ning/billing/account/dao/IFieldStoreDao.sql.stg b/account/src/main/resources/com/ning/billing/account/dao/IFieldStoreDao.sql.stg
index a34f815..f8fc7a5 100644
--- a/account/src/main/resources/com/ning/billing/account/dao/IFieldStoreDao.sql.stg
+++ b/account/src/main/resources/com/ning/billing/account/dao/IFieldStoreDao.sql.stg
@@ -1,17 +1,17 @@
 group IFieldStoreDao;
 
-createFields() ::= <<
+create() ::= <<
   INSERT INTO custom_fields(id, object_id, object_type, field_name, field_value)
   VALUES (:idAsString, :objectId, :objectType, :name, :value);
 >>
 
-saveFields() ::= <<
+update() ::= <<
     UPDATE custom_fields
     SET object_type = :objectType, object_id = :objectId, field_name = :name, field_value = :value
     WHERE id = :id;
 >>
 
-getFields() ::= <<
+load() ::= <<
     SELECT id, field_name, field_value
     FROM custom_fields
     WHERE object_id = :objectId AND object_type = :objectType;
diff --git a/account/src/main/resources/com/ning/billing/account/dao/ITagDao.sql.stg b/account/src/main/resources/com/ning/billing/account/dao/ITagDao.sql.stg
new file mode 100644
index 0000000..3a961a9
--- /dev/null
+++ b/account/src/main/resources/com/ning/billing/account/dao/ITagDao.sql.stg
@@ -0,0 +1,17 @@
+group TagDao;
+
+create() ::= <<
+
+>>
+
+update() ::= <<
+
+>>
+
+load() ::= <<
+
+>>
+;
+
+
+  id, tag_description_id, object_id, date_added, added_by
\ No newline at end of file
diff --git a/account/src/main/resources/com/ning/billing/account/dao/ITagDescriptionDao.sql.stg b/account/src/main/resources/com/ning/billing/account/dao/ITagDescriptionDao.sql.stg
new file mode 100644
index 0000000..7881bbf
--- /dev/null
+++ b/account/src/main/resources/com/ning/billing/account/dao/ITagDescriptionDao.sql.stg
@@ -0,0 +1,23 @@
+group TagDescriptionDao;
+
+create() ::= <<
+  INSERT INTO tag_descriptions(id, name, added_by, created, description, generate_invoice, process_payment)
+  VALUES(:id, :name, :addedBy, :created, :description, :generateInvoice, :processPayment)
+>>
+
+update() ::= <<
+  UPDATE tag_descriptions
+  SET name = :name, added_by = :addedBy, created :=created,
+      description := description, generateInvoice = :generateInvoice,
+      processPayment = :processPayment
+  WHERE id = :id;
+>>
+
+load() ::= <<
+  SELECT id, name, added_by, created, description, generate_invoice, process_payment
+  FROM tag_descriptions
+  WHERE id = :id;
+>>
+;
+
+
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 92f6bd0..6a9a23c 100644
--- a/account/src/main/resources/com/ning/billing/account/ddl.sql
+++ b/account/src/main/resources/com/ning/billing/account/ddl.sql
@@ -1,10 +1,14 @@
 DROP TABLE IF EXISTS accounts;
 CREATE TABLE accounts (
     id char(36) NOT NULL,
-    key_name varchar(128) NOT NULL,
-
+    external_key varchar(128) NULL,
+    email varchar(50) DEFAULT NULL,
+    name varchar(100) NOT NULL,
+    phone varchar(13) DEFAULT NULL,
+    currency char(3) NOT NULL,
     PRIMARY KEY(id)
 ) ENGINE=innodb;
+CREATE UNIQUE INDEX accounts_external_key ON accounts(external_key);
 
 DROP TABLE IF EXISTS custom_fields;
 CREATE TABLE custom_fields (
@@ -15,5 +19,30 @@ CREATE TABLE custom_fields (
   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,
+  added_by varchar(50) NOT NULL,
+  created datetime NOT NULL,
+  description varchar(200) NOT NULL,
+  generateInvoice boolean DEFAULT false,
+  processPayment boolean DEFAULT false,
+  PRIMARY KEY(id)
+) ENGINE=innodb;
+CREATE UNIQUE INDEX tag_descriptions_name ON tag_descriptions(name);
 
-CREATE INDEX custom_fields_object_id_object_type ON custom_fields(object_id, object_type);
\ No newline at end of file
+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,
+  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/account/src/test/java/com/ning/billing/account/dao/TestFieldStore.java b/account/src/test/java/com/ning/billing/account/dao/TestFieldStore.java
new file mode 100644
index 0000000..14640a2
--- /dev/null
+++ b/account/src/test/java/com/ning/billing/account/dao/TestFieldStore.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.account.dao;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Stage;
+import com.ning.billing.account.api.FieldStore;
+import com.ning.billing.account.glue.AccountModuleMock;
+import com.ning.billing.account.glue.InjectorMagic;
+import org.apache.commons.io.IOUtils;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.io.IOException;
+import java.util.UUID;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+@Test(groups={"Account-DAO"})
+public class TestFieldStore {
+    private InjectorMagic injectorMagic;
+
+    @BeforeClass(alwaysRun = true)
+    private void setup() throws IOException {
+        AccountModuleMock module = new AccountModuleMock();
+        final String ddl = IOUtils.toString(IAccountDaoSql.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
+        module.createDb(ddl);
+
+        // Healthcheck test to make sure MySQL is setup properly
+        try {
+            final Injector injector = Guice.createInjector(Stage.DEVELOPMENT, module);
+            injectorMagic = injector.getInstance(InjectorMagic.class);
+
+            IFieldStoreDao dao = injector.getInstance(IFieldStoreDao.class);
+            dao.test();
+        }
+        catch (Throwable t) {
+            fail(t.toString());
+        }
+    }
+
+    @Test
+    public void testFieldStore() {
+        UUID id = UUID.randomUUID();
+        String objectType = "Test widget";
+
+        FieldStore fieldStore = new FieldStore(id, objectType);
+
+        String fieldName = "TestField1";
+        String fieldValue = "Kitty Hawk";
+        fieldStore.setValue(fieldName, fieldValue);
+
+        fieldStore.save();
+
+        fieldStore = FieldStore.create(id, objectType);
+        fieldStore.load();
+
+        assertEquals(fieldStore.getValue(fieldName), fieldValue);
+    }
+}
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 3b4a687..355edbf 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
@@ -23,6 +23,7 @@ import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.IAccount;
 import com.ning.billing.account.glue.AccountModuleMock;
 import com.ning.billing.account.glue.InjectorMagic;
+import com.ning.billing.catalog.api.Currency;
 import org.apache.commons.io.IOUtils;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
@@ -36,7 +37,7 @@ import static org.testng.Assert.*;
 @Test(groups = {"Account", "Account-DAO"})
 public class TestSimpleAccountDao {
     private IAccountDao dao;
-    private InjectorMagic injectorMagic;
+    //private InjectorMagic injectorMagic;
 
     @BeforeClass(alwaysRun = true)
     private void setup() throws IOException {
@@ -48,7 +49,7 @@ public class TestSimpleAccountDao {
         try {
             final Injector injector = Guice.createInjector(Stage.DEVELOPMENT, module);
 
-            injectorMagic = injector.getInstance(InjectorMagic.class);
+            InjectorMagic injectorMagic = injector.getInstance(InjectorMagic.class);
             dao = injector.getInstance(IAccountDao.class);
             dao.test();
         }
@@ -57,19 +58,32 @@ public class TestSimpleAccountDao {
         }
     }
 
+    private final String key = "test1234";
+    private final String name = "Wesley";
+    private final String email = "dreadpirateroberts@therevenge.com";
+
+    private Account createTestAccount() {
+        Account account = Account.create();
+        String thisKey = key + UUID.randomUUID().toString();
+        String thisName = name + UUID.randomUUID().toString();
+        account.externalKey(thisKey).name(thisName).email(email).currency(Currency.USD);
+        return account;
+    }
+
     @Test(enabled=true, groups={"Account-DAO"})
     public void testBasic() {
 
-        IAccount a = new Account().withKey("foo");
-        dao.createAccount(a);
+        IAccount a = createTestAccount();
+        dao.saveAccount(a);
+        String key = a.getExternalKey();
 
-        IAccount r = dao.getAccountByKey("foo");
+        IAccount r = dao.getAccountByKey(key);
         assertNotNull(r);
-        assertEquals(r.getKey(), a.getKey());
+        assertEquals(r.getExternalKey(), a.getExternalKey());
 
         r = dao.getAccountById(r.getId());
         assertNotNull(r);
-        assertEquals(r.getKey(), a.getKey());
+        assertEquals(r.getExternalKey(), a.getExternalKey());
 
         List<IAccount> all = dao.getAccounts();
         assertNotNull(all);
@@ -78,33 +92,34 @@ public class TestSimpleAccountDao {
 
     @Test
     public void testGetById() {
-        String key = "test1234";
-
-        IAccount account = Account.create().withKey(key);
+        Account account = createTestAccount();
         UUID id = account.getId();
-
+        String key = account.getExternalKey();
+        String name = account.getName();
         account.save();
 
         account = Account.loadAccount(id);
         assertNotNull(account);
         assertEquals(account.getId(), id);
-        assertEquals(account.getKey(), key);
+        assertEquals(account.getExternalKey(), key);
+        assertEquals(account.getName(), name);
+
     }
 
     @Test
     public void testCustomFields() {
-        String key = "test45678";
-        IAccount account = Account.create().withKey(key);
-
+        Account account = createTestAccount();
         String fieldName = "testField1";
         String fieldValue = "testField1_value";
         account.setFieldValue(fieldName, fieldValue);
 
         account.save();
 
-        account = Account.loadAccount(key);
-        assertNotNull(account);
-        assertEquals(account.getKey(), key);
-        assertEquals(account.getFieldValue(fieldName), fieldValue);
+        Account thisAccount = Account.loadAccount(account.getExternalKey());
+        assertNotNull(thisAccount);
+        assertEquals(thisAccount.getExternalKey(), account.getExternalKey());
+        assertEquals(thisAccount.getFieldValue(fieldName), fieldValue);
     }
+
+
 }
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 63c7a06..1cb577f 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
@@ -99,7 +99,7 @@ public class AnalyticsListener implements IApiListener
         if (bundle != null) {
             transitionKey = bundle.getKey();
 
-            final IAccount account = accountApi.getAccountFromId(bundle.getAccountId());
+            final IAccount account = accountApi.getAccountById(bundle.getAccountId());
             if (account != null) {
                 currency = account.getCurrency();
             }
diff --git a/api/src/main/java/com/ning/billing/account/api/IAccount.java b/api/src/main/java/com/ning/billing/account/api/IAccount.java
index 41908bb..b141d80 100644
--- a/api/src/main/java/com/ning/billing/account/api/IAccount.java
+++ b/api/src/main/java/com/ning/billing/account/api/IAccount.java
@@ -16,16 +16,6 @@
 
 package com.ning.billing.account.api;
 
-import java.util.UUID;
+public interface IAccount extends IAccountData, IEntity {
 
-public interface IAccount extends IAccountData {
-    public UUID getId();
-
-    public void load();
-
-    public void save();
-
-    public String getFieldValue(String fieldName);
-
-    public void setFieldValue(String fieldName, String fieldValue);
 }
diff --git a/api/src/main/java/com/ning/billing/account/api/IAccountData.java b/api/src/main/java/com/ning/billing/account/api/IAccountData.java
index 5e2104d..dc85eb8 100644
--- a/api/src/main/java/com/ning/billing/account/api/IAccountData.java
+++ b/api/src/main/java/com/ning/billing/account/api/IAccountData.java
@@ -20,7 +20,7 @@ import com.ning.billing.catalog.api.Currency;
 
 public interface IAccountData {
 
-    public String getKey();
+    public String getExternalKey();
 
     public String getName();
 
diff --git a/api/src/main/java/com/ning/billing/account/api/IAccountService.java b/api/src/main/java/com/ning/billing/account/api/IAccountService.java
index fa5a9fe..d5769f2 100644
--- a/api/src/main/java/com/ning/billing/account/api/IAccountService.java
+++ b/api/src/main/java/com/ning/billing/account/api/IAccountService.java
@@ -19,6 +19,5 @@ package com.ning.billing.account.api;
 import com.ning.billing.lifecycle.IService;
 
 public interface IAccountService extends IService {
-
     public IAccountUserApi getAccountUserApi();
 }
diff --git a/api/src/main/java/com/ning/billing/account/api/IAccountUserApi.java b/api/src/main/java/com/ning/billing/account/api/IAccountUserApi.java
index fc91345..d56dca4 100644
--- a/api/src/main/java/com/ning/billing/account/api/IAccountUserApi.java
+++ b/api/src/main/java/com/ning/billing/account/api/IAccountUserApi.java
@@ -25,7 +25,7 @@ public interface IAccountUserApi {
 
     public IAccount getAccountByKey(String key);
 
-    public IAccount getAccountFromId(UUID uid);
+    public IAccount getAccountById(UUID uid);
 
     public List<IAccount> getAccounts();
 }
diff --git a/api/src/main/java/com/ning/billing/account/api/IEntity.java b/api/src/main/java/com/ning/billing/account/api/IEntity.java
new file mode 100644
index 0000000..175033d
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/account/api/IEntity.java
@@ -0,0 +1,29 @@
+/*
+ * 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 java.util.UUID;
+
+public interface IEntity {
+    public UUID getId();
+
+    public String getIdAsString();
+
+    public boolean isNew();
+
+    public void setAsSaved();
+}
diff --git a/api/src/main/java/com/ning/billing/account/api/IPersistable.java b/api/src/main/java/com/ning/billing/account/api/IPersistable.java
new file mode 100644
index 0000000..94a86c9
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/account/api/IPersistable.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.account.api;
+
+public interface IPersistable {
+    public void load();
+
+    public void save();
+}
diff --git a/api/src/main/java/com/ning/billing/invoice/api/IInvoice.java b/api/src/main/java/com/ning/billing/invoice/api/IInvoice.java
new file mode 100644
index 0000000..d643930
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/invoice/api/IInvoice.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.invoice.api;
+
+import com.ning.billing.catalog.api.Currency;
+import org.joda.time.DateTime;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
+
+public interface IInvoice {
+    boolean add(IInvoiceItem item);
+
+    boolean add(List<IInvoiceItem> items);
+
+    List<IInvoiceItem> getItems();
+
+    int getNumberOfItems();
+
+    UUID getId();
+
+    UUID getAccountId();
+
+    DateTime getInvoiceDate();
+
+    DateTime getTargetDate();
+
+    Currency getCurrency();
+
+    DateTime getLastPaymentAttempt();
+
+    BigDecimal getAmountPaid();
+
+    BigDecimal getTotalAmount();
+
+    BigDecimal getAmountOutstanding();
+}
diff --git a/api/src/main/java/com/ning/billing/invoice/api/IInvoiceItem.java b/api/src/main/java/com/ning/billing/invoice/api/IInvoiceItem.java
new file mode 100644
index 0000000..ede9ce8
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/invoice/api/IInvoiceItem.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.invoice.api;
+
+import com.ning.billing.catalog.api.Currency;
+import org.joda.time.DateTime;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+public interface IInvoiceItem extends Comparable<IInvoiceItem> {
+    UUID getId();
+
+    UUID getInvoiceId();
+
+    UUID getSubscriptionId();
+
+    DateTime getStartDate();
+
+    DateTime getEndDate();
+
+    String getDescription();
+
+    BigDecimal getAmount();
+
+    BigDecimal getRate();
+
+    Currency getCurrency();
+
+    IInvoiceItem asCredit(UUID invoiceId);
+
+    int compareTo(IInvoiceItem invoiceItem);
+
+    void subtract(IInvoiceItem that);
+
+    boolean duplicates(IInvoiceItem that);
+
+    boolean cancels(IInvoiceItem that);
+}
diff --git a/api/src/main/java/com/ning/billing/invoice/api/IInvoiceService.java b/api/src/main/java/com/ning/billing/invoice/api/IInvoiceService.java
new file mode 100644
index 0000000..631f099
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/invoice/api/IInvoiceService.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.invoice.api;
+
+import com.ning.billing.lifecycle.IService;
+
+public interface IInvoiceService extends IService {
+    public IInvoiceUserApi getInvoiceUserApi();
+}
diff --git a/api/src/main/java/com/ning/billing/invoice/api/IInvoiceUserApi.java b/api/src/main/java/com/ning/billing/invoice/api/IInvoiceUserApi.java
new file mode 100644
index 0000000..3f9b8f4
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/invoice/api/IInvoiceUserApi.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.invoice.api;
+
+import org.joda.time.DateTime;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
+
+public interface IInvoiceUserApi {
+    public List<UUID> getInvoicesForPayment(DateTime targetDate, int numberOfDays);
+
+    public List<IInvoice> getInvoicesByAccount();
+
+    public IInvoice getInvoice(UUID invoiceId);
+
+    public void paymentAttemptFailed(UUID invoiceId, DateTime paymentAttemptDate);
+
+    public void paymentAttemptSuccessful(UUID invoiceId, DateTime paymentAttemptDate, BigDecimal paymentAmount);
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java
index d97756f..568d08a 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java
@@ -283,7 +283,7 @@ public abstract class TestUserApiBase {
                 return "4152876341";
             }
             @Override
-            public String getKey() {
+            public String getExternalKey() {
                 return "k123456";
             }
             @Override
@@ -294,25 +294,25 @@ public abstract class TestUserApiBase {
             public Currency getCurrency() {
                 return Currency.USD;
             }
-
             @Override
             public UUID getId() {
                 return UUID.randomUUID();
             }
 
             @Override
-            public void load() {}
-
-            @Override
-            public void save() {}
+            public String getIdAsString() {
+                return null;  //To change body of implemented methods use File | Settings | File Templates.
+            }
 
             @Override
-            public String getFieldValue(String fieldName) {
-                return null;
+            public boolean isNew() {
+                return false;  //To change body of implemented methods use File | Settings | File Templates.
             }
 
             @Override
-            public void setFieldValue(String fieldName, String fieldValue) {}
+            public void setAsSaved() {
+                //To change body of implemented methods use File | Settings | File Templates.
+            }
         };
         return account;
     }

invoice/pom.xml 36(+36 -0)

diff --git a/invoice/pom.xml b/invoice/pom.xml
index 3e589ad..919e422 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -29,20 +29,56 @@
             <artifactId>killbill-util</artifactId>
         </dependency>
         <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-util</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.testng</groupId>
             <artifactId>testng</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>management</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>management-dbfiles</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>commons-io</groupId>
             <artifactId>commons-io</artifactId>
             <version>2.0</version>
         </dependency>
         <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.jdbi</groupId>
             <artifactId>jdbi</artifactId>
             <version>2.27</version>
         </dependency>
+        <dependency>
+            <groupId>org.antlr</groupId>
+            <artifactId>stringtemplate</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.inject</groupId>
+            <artifactId>guice</artifactId>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
     <build>
     </build>
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/InvoiceService.java b/invoice/src/main/java/com/ning/billing/invoice/api/InvoiceService.java
new file mode 100644
index 0000000..247a3c6
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/InvoiceService.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.invoice.api;
+
+import com.google.inject.Inject;
+import com.ning.billing.lifecycle.LyfecycleHandlerType;
+
+public class InvoiceService implements IInvoiceService {
+    private static final String INVOICE_SERVICE_NAME = "invoice-service";
+    private final IInvoiceUserApi invoiceApi;
+
+    @Inject
+    public InvoiceService(IInvoiceUserApi api) {
+        this.invoiceApi = api;
+    }
+
+    @Override
+    public String getName() {
+        return INVOICE_SERVICE_NAME;
+    }
+
+    @Override
+    public IInvoiceUserApi getInvoiceUserApi() {
+        return invoiceApi;
+    }
+
+    @LyfecycleHandlerType(LyfecycleHandlerType.LyfecycleLevel.INIT_SERVICE)
+    public void initialize() {
+    }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java
new file mode 100644
index 0000000..8a4fc22
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.invoice.api;
+
+import com.google.inject.Inject;
+import com.ning.billing.invoice.dao.IInvoiceDao;
+import org.joda.time.DateTime;
+import org.skife.jdbi.v2.IDBI;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
+
+public class InvoiceUserApi implements IInvoiceUserApi {
+    private final IInvoiceDao dao;
+
+    @Inject
+    public InvoiceUserApi(IDBI dbi) {
+        dao = dbi.onDemand(IInvoiceDao.class);
+    }
+
+    @Override
+    public List<UUID> getInvoicesForPayment(DateTime targetDate, int numberOfDays) {
+        return dao.getInvoicesForPayment(targetDate.toDate(), numberOfDays);
+    }
+
+    @Override
+    public List<IInvoice> getInvoicesByAccount() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public IInvoice getInvoice(UUID invoiceId) {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public void paymentAttemptFailed(UUID invoiceId, DateTime paymentAttemptDate) {
+        //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public void paymentAttemptSuccessful(UUID invoiceId, DateTime paymentAttemptDate, BigDecimal paymentAmount) {
+        //To change body of implemented methods use File | Settings | File Templates.
+    }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/IInvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/IInvoiceDao.java
new file mode 100644
index 0000000..7840454
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/IInvoiceDao.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.invoice.dao;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.IInvoice;
+import com.ning.billing.invoice.api.IInvoiceItem;
+import com.ning.billing.invoice.model.Invoice;
+import org.joda.time.DateTime;
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.sqlobject.*;
+import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+import org.skife.jdbi.v2.tweak.ResultSetMapper;
+
+import java.lang.annotation.*;
+import java.math.BigDecimal;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+
+@ExternalizedSqlViaStringTemplate3()
+@RegisterMapper({UuidMapper.class, IInvoiceDao.InvoiceMapper.class})
+public interface IInvoiceDao {
+    @SqlQuery
+    List<IInvoice> getInvoicesByAccount(@Bind("accountId") final String accountId);
+
+    @SqlQuery
+    IInvoice getInvoice(@Bind("id") final String invoiceId);
+
+    @SqlUpdate
+    void createInvoice(@InvoiceBinder final IInvoice invoice);
+
+    @SqlQuery
+    List<IInvoice> getInvoicesBySubscription(@Bind("subscriptionId") final String subscriptionId);
+
+    @SqlQuery
+    List<UUID> getInvoicesForPayment(@Bind("targetDate") final Date targetDate,
+                                     @Bind("numberOfDays") final int numberOfDays);
+
+    @SqlUpdate
+    void notifySuccessfulPayment(@Bind("id") final String invoiceId,
+                                 @Bind("paymentDate") final Date paymentDate,
+                                 @Bind("paymentAmount") final BigDecimal paymentAmount);
+
+    @SqlUpdate
+    void notifyFailedPayment(@Bind("id") final String invoiceId,
+                             @Bind("paymentAttemptDate") final Date paymentAttemptDate);
+
+    @SqlUpdate
+    void test();
+
+    @BindingAnnotation(InvoiceBinder.InvoiceBinderFactory.class)
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.PARAMETER})
+    public @interface InvoiceBinder {
+        public static class InvoiceBinderFactory implements BinderFactory {
+            public Binder build(Annotation annotation) {
+                return new Binder<InvoiceBinder, IInvoice>() {
+                    public void bind(SQLStatement q, InvoiceBinder bind, IInvoice invoice) {
+                        q.bind("id", invoice.getId().toString());
+                        q.bind("accountId", invoice.getAccountId().toString());
+                        q.bind("invoiceDate", invoice.getInvoiceDate().toDate());
+                        q.bind("targetDate", invoice.getTargetDate().toDate());
+                        q.bind("amountPaid", invoice.getAmountPaid());
+                        q.bind("amountOutstanding", invoice.getAmountOutstanding());
+                        DateTime invoiceDate = invoice.getLastPaymentAttempt();
+                        q.bind("lastPaymentAttempt", invoiceDate == null ? null : invoiceDate.toDate());
+                        q.bind("currency", invoice.getCurrency().toString());
+                    }
+                };
+            }
+        }
+    }
+
+    public static class InvoiceMapper implements ResultSetMapper<IInvoice> {
+        @Override
+        public IInvoice map(int index, ResultSet result, StatementContext context) throws SQLException {
+            UUID id = UUID.fromString(result.getString("id"));
+            UUID accountId = UUID.fromString(result.getString("account_id"));
+            DateTime invoiceDate = new DateTime(result.getTimestamp("invoice_date"));
+            DateTime targetDate = new DateTime(result.getTimestamp("target_date"));
+            BigDecimal amountPaid = result.getBigDecimal("amount_paid");
+            Timestamp lastPaymentAttemptTimeStamp = result.getTimestamp("last_payment_attempt");
+            DateTime lastPaymentAttempt = lastPaymentAttemptTimeStamp == null ? null : new DateTime(lastPaymentAttemptTimeStamp);
+            Currency currency = Currency.valueOf(result.getString("currency"));
+
+            return new Invoice(id, accountId, invoiceDate, targetDate, currency, lastPaymentAttempt, amountPaid, new ArrayList<IInvoiceItem>());
+        }
+    }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/IInvoiceItemDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/IInvoiceItemDao.java
new file mode 100644
index 0000000..03202da
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/IInvoiceItemDao.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.invoice.dao;
+
+import com.ning.billing.invoice.api.IInvoiceItem;
+import com.ning.billing.invoice.model.InvoiceItemList;
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.sqlobject.*;
+import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+
+import java.lang.annotation.*;
+
+@ExternalizedSqlViaStringTemplate3()
+@RegisterMapper(InvoiceItemMapper.class)
+public interface IInvoiceItemDao {
+    @SqlQuery
+    InvoiceItemList getInvoiceItemsByInvoice(@Bind("invoiceId") final String invoiceId);
+
+    @SqlQuery
+    InvoiceItemList getInvoiceItemsByAccount(@Bind("accountId") final String accountId);
+
+    @SqlUpdate
+    void createInvoiceItem(@InvoiceItemBinder final IInvoiceItem invoiceItem);
+
+    @BindingAnnotation(InvoiceItemBinder.InvoiceItemBinderFactory.class)
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.PARAMETER})
+    public @interface InvoiceItemBinder {
+        public static class InvoiceItemBinderFactory implements BinderFactory {
+            public Binder build(Annotation annotation) {
+                return new Binder<InvoiceItemBinder, IInvoiceItem>() {
+                    public void bind(SQLStatement q, InvoiceItemBinder bind, IInvoiceItem item) {
+                        q.bind("id", item.getId().toString());
+                        q.bind("invoiceId", item.getInvoiceId().toString());
+                        q.bind("subscriptionId", item.getSubscriptionId().toString());
+                        q.bind("startDate", item.getStartDate().toDate());
+                        q.bind("endDate", item.getEndDate().toDate());
+                        q.bind("description", item.getDescription());
+                        q.bind("amount", item.getAmount());
+                        q.bind("rate", item.getRate());
+                        q.bind("currency", item.getCurrency().toString());
+                    }
+                };
+            }
+        }
+    }
+}
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 9abc12a..25ace96 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
@@ -16,32 +16,60 @@
 
 package com.ning.billing.invoice.dao;
 
-import com.ning.billing.invoice.model.Invoice;
-import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.BindBean;
-import org.skife.jdbi.v2.sqlobject.SqlQuery;
-import org.skife.jdbi.v2.sqlobject.SqlUpdate;
-import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+import com.google.inject.Inject;
+import com.ning.billing.invoice.api.IInvoice;
+import org.skife.jdbi.v2.IDBI;
 
 import java.math.BigDecimal;
+import java.util.Date;
 import java.util.List;
+import java.util.UUID;
 
-@ExternalizedSqlViaStringTemplate3()
-@RegisterMapper(InvoiceMapper.class)
-public interface InvoiceDao {
-    @SqlQuery
-    List<Invoice> getInvoicesByAccount(@Bind final String accountId);
+public class InvoiceDao implements IInvoiceDao {
+    private final IInvoiceDao dao;
 
-    @SqlQuery
-    Invoice getInvoice(@Bind final String invoiceId);
+    @Inject
+    public InvoiceDao(IDBI dbi) {
+        this.dao = dbi.onDemand(IInvoiceDao.class);
+    }
 
-    @SqlUpdate
-    void createInvoice(@BindBean final Invoice invoice);
+    @Override
+    public List<IInvoice> getInvoicesByAccount(final String accountId) {
+        return dao.getInvoicesByAccount(accountId);
+    }
 
-    @SqlUpdate
-    void addPayment(@Bind final String invoiceId, @Bind final BigDecimal paymentAmount);
+    @Override
+    public IInvoice getInvoice(final String invoiceId) {
+        return dao.getInvoice(invoiceId);
+    }
 
-    @SqlQuery
-    int test();
+    @Override
+    public void createInvoice(final IInvoice invoice) {
+        dao.createInvoice(invoice);
+    }
+
+    @Override
+    public List<IInvoice> getInvoicesBySubscription(String subscriptionId) {
+        return dao.getInvoicesBySubscription(subscriptionId);
+    }
+
+    @Override
+    public List<UUID> getInvoicesForPayment(Date targetDate, int numberOfDays) {
+        return dao.getInvoicesForPayment(targetDate, numberOfDays);
+    }
+
+    @Override
+    public void notifySuccessfulPayment(String invoiceId, Date paymentDate, BigDecimal paymentAmount) {
+        dao.notifySuccessfulPayment(invoiceId, paymentDate, paymentAmount);
+    }
+
+    @Override
+    public void notifyFailedPayment(String invoiceId, Date paymentAttemptDate) {
+        dao.notifyFailedPayment(invoiceId, paymentAttemptDate);
+    }
+
+    @Override
+    public void test() {
+        dao.test();
+    }
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemDao.java
index 9de9f76..91c7513 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemDao.java
@@ -16,24 +16,31 @@
 
 package com.ning.billing.invoice.dao;
 
-import com.ning.billing.invoice.model.InvoiceItem;
+import com.google.inject.Inject;
+import com.ning.billing.invoice.api.IInvoiceItem;
 import com.ning.billing.invoice.model.InvoiceItemList;
-import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.BindBean;
-import org.skife.jdbi.v2.sqlobject.SqlQuery;
-import org.skife.jdbi.v2.sqlobject.SqlUpdate;
-import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+import org.skife.jdbi.v2.IDBI;
 
-@ExternalizedSqlViaStringTemplate3()
-@RegisterMapper(InvoiceItemMapper.class)
-public interface InvoiceItemDao {
-    @SqlQuery
-    InvoiceItemList getInvoiceItemsByInvoice(@Bind final String invoiceId);
+public class InvoiceItemDao implements IInvoiceItemDao {
+    private final IInvoiceItemDao dao;
 
-    @SqlQuery
-    InvoiceItemList getInvoiceItemsByAccount(@Bind final String accountId);
+    @Inject
+    public InvoiceItemDao(IDBI dbi) {
+        dao = dbi.onDemand(IInvoiceItemDao.class);
+    }
 
-    @SqlUpdate
-    void createInvoiceItem(@BindBean final InvoiceItem invoiceItem);
+    @Override
+    public InvoiceItemList getInvoiceItemsByInvoice(String invoiceId) {
+        return dao.getInvoiceItemsByInvoice(invoiceId);
+    }
+
+    @Override
+    public InvoiceItemList getInvoiceItemsByAccount(String accountId) {
+        return dao.getInvoiceItemsByAccount(accountId);
+    }
+
+    @Override
+    public void createInvoiceItem(IInvoiceItem invoiceItem) {
+        dao.createInvoiceItem(invoiceItem);
+    }
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/UuidMapper.java b/invoice/src/main/java/com/ning/billing/invoice/dao/UuidMapper.java
new file mode 100644
index 0000000..4affe3d
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/UuidMapper.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.invoice.dao;
+
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.tweak.ResultSetMapper;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.UUID;
+
+public class UuidMapper implements ResultSetMapper<UUID> {
+    @Override
+    public UUID map(final int index, ResultSet resultSet, StatementContext statementContext) throws SQLException {
+        return UUID.fromString(resultSet.getString(1));
+    }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/glue/InjectorMagic.java b/invoice/src/main/java/com/ning/billing/invoice/glue/InjectorMagic.java
new file mode 100644
index 0000000..790d3af
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/glue/InjectorMagic.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.invoice.glue;
+
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.ning.billing.invoice.dao.InvoiceDao;
+
+public class InjectorMagic {
+    public static InjectorMagic instance;
+
+    private final Injector injector;
+
+    @Inject
+    public InjectorMagic(Injector injector) {
+        this.injector = injector;
+        synchronized(InjectorMagic.class) {
+            if (instance == null) {
+                instance = this;
+            }
+        }
+    }
+
+    public static Injector get() {
+        if (instance == null) {
+            throw new RuntimeException("Trying to retrieve injector too early");
+        }
+        return instance.getInjector();
+    }
+
+    private Injector getInjector() {
+        return injector;
+    }
+
+    public static InvoiceDao getInvoiceDao() {
+        return InjectorMagic.get().getInstance(InvoiceDao.class);
+    }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/glue/InvoiceModule.java b/invoice/src/main/java/com/ning/billing/invoice/glue/InvoiceModule.java
new file mode 100644
index 0000000..bd1932f
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/glue/InvoiceModule.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.invoice.glue;
+
+import com.google.inject.AbstractModule;
+import com.ning.billing.invoice.api.IInvoiceUserApi;
+import com.ning.billing.invoice.api.InvoiceUserApi;
+import com.ning.billing.invoice.dao.IInvoiceDao;
+import com.ning.billing.invoice.dao.IInvoiceItemDao;
+import com.ning.billing.invoice.dao.InvoiceDao;
+import com.ning.billing.invoice.dao.InvoiceItemDao;
+
+public class InvoiceModule extends AbstractModule {
+    private void installInvoiceDao() {
+        bind(IInvoiceDao.class).to(InvoiceDao.class).asEagerSingleton();
+    }
+
+    private void installInvoiceItemDao() {
+        bind(IInvoiceItemDao.class).to(InvoiceItemDao.class).asEagerSingleton();
+    }
+
+    protected void installInvoiceUserApi() {
+        bind(IInvoiceUserApi.class).to(InvoiceUserApi.class).asEagerSingleton();
+    }
+
+    @Override
+    protected void configure() {
+        installInvoiceDao();
+        installInvoiceItemDao();
+    }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
index 730c22b..9526b62 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
@@ -21,6 +21,8 @@ import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.entitlement.api.billing.BillingMode;
 import com.ning.billing.entitlement.api.billing.IBillingEvent;
 import com.ning.billing.invoice.api.BillingEventSet;
+import com.ning.billing.invoice.api.IInvoice;
+import com.ning.billing.invoice.api.IInvoiceItem;
 import org.joda.time.DateTime;
 
 import java.math.BigDecimal;
@@ -31,13 +33,13 @@ import java.util.UUID;
 
 public class DefaultInvoiceGenerator implements IInvoiceGenerator {
     @Override
-    public Invoice generateInvoice(final UUID accountId, final BillingEventSet events, final InvoiceItemList existingItems, final DateTime targetDate, final Currency targetCurrency) {
-        if (events == null) {return new Invoice(accountId, targetCurrency);}
-        if (events.size() == 0) {return new Invoice(accountId, targetCurrency);}
+    public IInvoice generateInvoice(final UUID accountId, final BillingEventSet events, final InvoiceItemList existingItems, final DateTime targetDate, final Currency targetCurrency) {
+        if (events == null) {return new Invoice(accountId, targetDate, targetCurrency);}
+        if (events.size() == 0) {return new Invoice(accountId, targetDate, targetCurrency);}
 
-        Invoice invoice = new Invoice(accountId, targetCurrency);
-        InvoiceItemList currentItems = generateInvoiceItems(events, invoice.getInvoiceId(), targetDate, targetCurrency);
-        InvoiceItemList itemsToPost = reconcileInvoiceItems(invoice.getInvoiceId(), currentItems, existingItems);
+        Invoice invoice = new Invoice(accountId, targetDate, targetCurrency);
+        InvoiceItemList currentItems = generateInvoiceItems(events, invoice.getId(), targetDate, targetCurrency);
+        InvoiceItemList itemsToPost = reconcileInvoiceItems(invoice.getId(), currentItems, existingItems);
         invoice.add(itemsToPost);
 
         return invoice;
@@ -45,7 +47,7 @@ public class DefaultInvoiceGenerator implements IInvoiceGenerator {
 
     private InvoiceItemList reconcileInvoiceItems(final UUID invoiceId, final InvoiceItemList currentInvoiceItems, final InvoiceItemList existingInvoiceItems) {
         InvoiceItemList currentItems = new InvoiceItemList();
-        for (InvoiceItem item : currentInvoiceItems) {
+        for (IInvoiceItem item : currentInvoiceItems) {
             currentItems.add(new InvoiceItem(item, invoiceId));
         }
 
@@ -54,11 +56,11 @@ public class DefaultInvoiceGenerator implements IInvoiceGenerator {
         Collections.sort(currentItems);
         Collections.sort(existingItems);
 
-        List<InvoiceItem> existingItemsToRemove = new ArrayList<InvoiceItem>();
+        List<IInvoiceItem> existingItemsToRemove = new ArrayList<IInvoiceItem>();
 
-        for (InvoiceItem currentItem : currentItems) {
+        for (IInvoiceItem currentItem : currentItems) {
             // see if there are any existing items that are covered by the current item
-            for (InvoiceItem existingItem : existingItems) {
+            for (IInvoiceItem existingItem : existingItems) {
                 if (currentItem.duplicates(existingItem)) {
                     currentItem.subtract(existingItem);
                     existingItemsToRemove.add(existingItem);
@@ -75,7 +77,7 @@ public class DefaultInvoiceGenerator implements IInvoiceGenerator {
         currentItems.removeZeroDollarItems();
 
         // add existing items that aren't covered by current items as credit items
-        for (InvoiceItem existingItem : existingItems) {
+        for (IInvoiceItem existingItem : existingItems) {
             currentItems.add(existingItem.asCredit(invoiceId));
         }
 
@@ -109,7 +111,7 @@ public class DefaultInvoiceGenerator implements IInvoiceGenerator {
         return items;
     }
 
-    private void processEvent(UUID invoiceId, IBillingEvent event, List<InvoiceItem> items, DateTime targetDate, Currency targetCurrency) {
+    private void processEvent(UUID invoiceId, IBillingEvent event, List<IInvoiceItem> items, DateTime targetDate, Currency targetCurrency) {
         BigDecimal rate = event.getPrice(targetCurrency);
         BigDecimal invoiceItemAmount = calculateInvoiceItemAmount(event, targetDate, rate);
         IBillingMode billingMode = getBillingMode(event.getBillingMode());
@@ -118,7 +120,7 @@ public class DefaultInvoiceGenerator implements IInvoiceGenerator {
         addInvoiceItem(invoiceId, items, event, billThroughDate, invoiceItemAmount, rate, targetCurrency);
     }
 
-    private void processEvents(UUID invoiceId, IBillingEvent firstEvent, IBillingEvent secondEvent, List<InvoiceItem> items, DateTime targetDate, Currency targetCurrency) {
+    private void processEvents(UUID invoiceId, IBillingEvent firstEvent, IBillingEvent secondEvent, List<IInvoiceItem> items, DateTime targetDate, Currency targetCurrency) {
         BigDecimal rate = firstEvent.getPrice(targetCurrency);
         BigDecimal invoiceItemAmount = calculateInvoiceItemAmount(firstEvent, secondEvent, targetDate, rate);
         IBillingMode billingMode = getBillingMode(firstEvent.getBillingMode());
@@ -127,9 +129,9 @@ public class DefaultInvoiceGenerator implements IInvoiceGenerator {
         addInvoiceItem(invoiceId, items, firstEvent, billThroughDate, invoiceItemAmount, rate, targetCurrency);
     }
 
-    private void addInvoiceItem(UUID invoiceId, List<InvoiceItem> items, IBillingEvent event, DateTime billThroughDate, BigDecimal amount, BigDecimal rate, Currency currency) {
+    private void addInvoiceItem(UUID invoiceId, List<IInvoiceItem> items, IBillingEvent event, DateTime billThroughDate, BigDecimal amount, BigDecimal rate, Currency currency) {
         if (!(amount.compareTo(BigDecimal.ZERO) == 0)) {
-            InvoiceItem item = new InvoiceItem(invoiceId, event.getSubscriptionId(), event.getEffectiveDate(), billThroughDate, event.getDescription(), amount, rate, currency);
+            IInvoiceItem item = new InvoiceItem(invoiceId, event.getSubscriptionId(), event.getEffectiveDate(), billThroughDate, event.getDescription(), amount, rate, currency);
             items.add(item);
         }
     }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/IInvoiceGenerator.java b/invoice/src/main/java/com/ning/billing/invoice/model/IInvoiceGenerator.java
index 09d5e47..934b11a 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/IInvoiceGenerator.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/IInvoiceGenerator.java
@@ -18,11 +18,12 @@ package com.ning.billing.invoice.model;
 
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.BillingEventSet;
+import com.ning.billing.invoice.api.IInvoice;
 import org.joda.time.DateTime;
 
 import java.util.UUID;
 
 // TODO: Jeff -- Determine what the consequence of account-level currency changes are on repair scenarios
 public interface IInvoiceGenerator {
-    public Invoice generateInvoice(UUID accountId, BillingEventSet events, InvoiceItemList items, DateTime targetDate, Currency targetCurrency);
+    public IInvoice generateInvoice(UUID accountId, BillingEventSet events, InvoiceItemList items, DateTime targetDate, Currency targetCurrency);
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/Invoice.java b/invoice/src/main/java/com/ning/billing/invoice/model/Invoice.java
index 8d67d53..3b20696 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/Invoice.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/Invoice.java
@@ -17,73 +17,111 @@
 package com.ning.billing.invoice.model;
 
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.IInvoice;
+import com.ning.billing.invoice.api.IInvoiceItem;
+import com.ning.billing.util.clock.Clock;
 import org.joda.time.DateTime;
 
 import java.math.BigDecimal;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
-public class Invoice {
+public class Invoice implements IInvoice {
     private final InvoiceItemList items = new InvoiceItemList();
-    private final UUID invoiceId;
+    private final UUID id;
     private UUID accountId;
     private final DateTime invoiceDate;
+    private final DateTime targetDate;
     private Currency currency;
+    private BigDecimal amountPaid;
+    private DateTime lastPaymentAttempt;
 
-    public Invoice() {
-        this.invoiceId = UUID.randomUUID();
-        this.invoiceDate = new DateTime();
+    public Invoice(UUID accountId, DateTime targetDate, Currency currency) {
+        this(UUID.randomUUID(), accountId, new Clock().getUTCNow(), targetDate, currency, null, BigDecimal.ZERO, new ArrayList<IInvoiceItem>());
     }
 
-    public Invoice(UUID accountId, Currency currency) {
-        this.invoiceId = UUID.randomUUID();
-        this.accountId = accountId;
-        this.invoiceDate = new DateTime();
-        this.currency = currency;
+    public Invoice(UUID invoiceId, UUID accountId, DateTime invoiceDate, DateTime targetDate,
+                   Currency currency, DateTime lastPaymentAttempt, BigDecimal amountPaid) {
+        this(invoiceId, accountId, invoiceDate, targetDate, currency, lastPaymentAttempt, amountPaid, new ArrayList<IInvoiceItem>());
     }
 
-    public Invoice(UUID accountId, List<InvoiceItem> items, Currency currency) {
-        this.invoiceId = UUID.randomUUID();
+    public Invoice(UUID invoiceId, UUID accountId, DateTime invoiceDate, DateTime targetDate,
+                   Currency currency, DateTime lastPaymentAttempt, BigDecimal amountPaid,
+                   List<IInvoiceItem> invoiceItems) {
+        this.id = invoiceId;
         this.accountId = accountId;
-        this.invoiceDate = new DateTime();
+        this.invoiceDate = invoiceDate;
+        this.targetDate = targetDate;
         this.currency = currency;
-        this.items.addAll(items);
+        this.lastPaymentAttempt= lastPaymentAttempt;
+        this.amountPaid = amountPaid;
+        this.items.addAll(invoiceItems);
     }
 
-    public boolean add(InvoiceItem item) {
+    @Override
+    public boolean add(IInvoiceItem item) {
         return items.add(item);
     }
 
-    public boolean add(List<InvoiceItem> items) {
+    @Override
+    public boolean add(List<IInvoiceItem> items) {
         return this.items.addAll(items);
     }
 
-    public List<InvoiceItem> getItems() {
+    @Override
+    public List<IInvoiceItem> getItems() {
         return items;
     }
 
+    @Override
     public int getNumberOfItems() {
         return items.size();
     }
 
-    public UUID getInvoiceId() {
-        return invoiceId;
+    @Override
+    public UUID getId() {
+        return id;
     }
 
+    @Override
     public UUID getAccountId() {
         return accountId;
     }
 
+    @Override
     public DateTime getInvoiceDate() {
         return invoiceDate;
     }
 
+    @Override
+    public DateTime getTargetDate() {
+        return targetDate;
+    }
+
+    @Override
     public Currency getCurrency() {
         return currency;
     }
 
+    @Override
+    public DateTime getLastPaymentAttempt() {
+        return lastPaymentAttempt;
+    }
+
+    @Override
+    public BigDecimal getAmountPaid() {
+        return amountPaid;
+    }
+
+    @Override
     public BigDecimal getTotalAmount() {
         return items.getTotalAmount();
     }
+
+    @Override
+    public BigDecimal getAmountOutstanding() {
+        return getTotalAmount().subtract(getAmountPaid());
+    }
 }
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItem.java
index 7645575..07ff7a1 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItem.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItem.java
@@ -17,13 +17,14 @@
 package com.ning.billing.invoice.model;
 
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.IInvoiceItem;
 import org.joda.time.DateTime;
 
 import java.math.BigDecimal;
 import java.util.UUID;
 
-public class InvoiceItem implements Comparable<InvoiceItem> {
-    private final UUID invoiceItemId;
+public class InvoiceItem implements IInvoiceItem {
+    private final UUID id;
     private final UUID invoiceId;
     private final UUID subscriptionId;
     private DateTime startDate;
@@ -34,7 +35,7 @@ public class InvoiceItem implements Comparable<InvoiceItem> {
     private final Currency currency;
 
     public InvoiceItem(UUID invoiceId, UUID subscriptionId, DateTime startDate, DateTime endDate, String description, BigDecimal amount, BigDecimal rate, Currency currency) {
-        this.invoiceItemId = UUID.randomUUID();
+        this.id = UUID.randomUUID();
         this.invoiceId = invoiceId;
         this.subscriptionId = subscriptionId;
         this.startDate = startDate;
@@ -45,48 +46,70 @@ public class InvoiceItem implements Comparable<InvoiceItem> {
         this.currency = currency;
     }
 
-    public InvoiceItem(InvoiceItem that, UUID invoiceId) {
-        this.invoiceItemId = UUID.randomUUID();
+    public InvoiceItem(IInvoiceItem that, UUID invoiceId) {
+        this.id = UUID.randomUUID();
         this.invoiceId = invoiceId;
-        this.subscriptionId = that.subscriptionId;
-        this.startDate = that.startDate;
-        this.endDate = that.endDate;
-        this.description = that.description;
-        this.amount = that.amount;
-        this.rate = that.rate;
-        this.currency = that.currency;
+        this.subscriptionId = that.getSubscriptionId();
+        this.startDate = that.getStartDate();
+        this.endDate = that.getEndDate();
+        this.description = that.getDescription();
+        this.amount = that.getAmount();
+        this.rate = that.getRate();
+        this.currency = that.getCurrency();
     }
 
-    public InvoiceItem asCredit(UUID invoiceId) {
+    @Override
+    public IInvoiceItem asCredit(UUID invoiceId) {
         return new InvoiceItem(invoiceId, subscriptionId, startDate, endDate, description, amount.negate(), rate, currency);
     }
 
+    @Override
+    public UUID getId() {
+        return id;
+    }
+
+    @Override
+    public UUID getInvoiceId() {
+        return invoiceId;
+    }
+
+    @Override
     public UUID getSubscriptionId() {
         return subscriptionId;
     }
 
+    @Override
     public DateTime getStartDate() {
         return startDate;
     }
 
+    @Override
     public DateTime getEndDate() {
         return endDate;
     }
 
+    @Override
+    public String getDescription() {
+        return description;
+    }
+
+    @Override
     public BigDecimal getAmount() {
         return amount;
     }
 
+    @Override
     public BigDecimal getRate() {
         return rate;
     }
 
+    @Override
     public Currency getCurrency() {
         return currency;
     }
 
     @Override
-    public int compareTo(InvoiceItem invoiceItem) {
+    public int compareTo(IInvoiceItem invoiceItem) {
         int compareSubscriptions = getSubscriptionId().compareTo(invoiceItem.getSubscriptionId());
 
         if (compareSubscriptions == 0) {
@@ -97,24 +120,26 @@ public class InvoiceItem implements Comparable<InvoiceItem> {
     }
 
     // TODO: deal with error cases
-    public void subtract(InvoiceItem that) {
-        if (this.startDate.equals(that.startDate) && this.endDate.equals(that.endDate)) {
+    @Override
+    public void subtract(IInvoiceItem that) {
+        if (this.startDate.equals(that.getStartDate()) && this.endDate.equals(that.getEndDate())) {
             this.startDate = this.endDate;
-            this.amount = this.amount.subtract(that.amount);
+            this.amount = this.amount.subtract(that.getAmount());
         } else {
-            if (this.startDate.equals(that.startDate)) {
-                this.startDate = that.endDate;
-                this.amount = this.amount.subtract(that.amount);
+            if (this.startDate.equals(that.getStartDate())) {
+                this.startDate = that.getEndDate();
+                this.amount = this.amount.subtract(that.getAmount());
             }
 
-            if (this.endDate.equals(that.endDate)) {
-                this.endDate = that.startDate;
-                this.amount = this.amount.subtract(that.amount);
+            if (this.endDate.equals(that.getEndDate())) {
+                this.endDate = that.getStartDate();
+                this.amount = this.amount.subtract(that.getAmount());
             }
         }
     }
 
-    public boolean duplicates(InvoiceItem that) {
+    @Override
+    public boolean duplicates(IInvoiceItem that) {
         if(!this.getSubscriptionId().equals(that.getSubscriptionId())) {return false;}
         if(!this.getRate().equals(that.getRate())) {return false;}
         if(!this.getCurrency().equals(that.getCurrency())) {return false;}
@@ -128,7 +153,8 @@ public class InvoiceItem implements Comparable<InvoiceItem> {
      * @param that
      * @return
      */
-    public boolean cancels(InvoiceItem that) {
+    @Override
+    public boolean cancels(IInvoiceItem that) {
         if(!this.getSubscriptionId().equals(that.getSubscriptionId())) {return false;}
         if(!this.getEndDate().equals(that.getEndDate())) {return false;}
         if(!this.getStartDate().equals(that.getStartDate())) {return false;}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemList.java b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemList.java
index 679c735..7d15f20 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemList.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemList.java
@@ -16,18 +16,20 @@
 
 package com.ning.billing.invoice.model;
 
+import com.ning.billing.invoice.api.IInvoiceItem;
+
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.List;
 
-public class InvoiceItemList extends ArrayList<InvoiceItem> {
+public class InvoiceItemList extends ArrayList<IInvoiceItem> {
     private static final int NUMBER_OF_DECIMALS = InvoicingConfiguration.getNumberOfDecimals();
 
     public BigDecimal getTotalAmount() {
         // TODO: Jeff -- naive implementation, assumes all invoice items share the same currency
         BigDecimal total = new BigDecimal("0");
 
-        for (InvoiceItem item : this) {
+        for (IInvoiceItem item : this) {
             total = total.add(item.getAmount());
         }
 
@@ -35,9 +37,9 @@ public class InvoiceItemList extends ArrayList<InvoiceItem> {
     }
 
     public void removeZeroDollarItems() {
-        List<InvoiceItem> itemsToRemove = new ArrayList<InvoiceItem>();
+        List<IInvoiceItem> itemsToRemove = new ArrayList<IInvoiceItem>();
 
-        for (InvoiceItem item : this) {
+        for (IInvoiceItem item : this) {
             if (item.getAmount().compareTo(BigDecimal.ZERO) == 0) {
                 itemsToRemove.add(item);
             }
@@ -47,12 +49,12 @@ public class InvoiceItemList extends ArrayList<InvoiceItem> {
     }
 
     public void removeCancellingPairs() {
-        List<InvoiceItem> itemsToRemove = new ArrayList<InvoiceItem>();
+        List<IInvoiceItem> itemsToRemove = new ArrayList<IInvoiceItem>();
 
         for (int firstItemIndex = 0; firstItemIndex < this.size(); firstItemIndex++) {
             for (int secondItemIndex = firstItemIndex + 1; secondItemIndex < this.size(); secondItemIndex++) {
-                InvoiceItem firstItem = this.get(firstItemIndex);
-                InvoiceItem secondItem = this.get(secondItemIndex);
+                IInvoiceItem firstItem = this.get(firstItemIndex);
+                IInvoiceItem secondItem = this.get(secondItemIndex);
                 if (firstItem.cancels(secondItem)) {
                     itemsToRemove.add(firstItem);
                     itemsToRemove.add(secondItem);
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/IInvoiceDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/IInvoiceDao.sql.stg
new file mode 100644
index 0000000..b22f792
--- /dev/null
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/IInvoiceDao.sql.stg
@@ -0,0 +1,53 @@
+group InvoiceDao;
+
+getInvoicesByAccount() ::= <<
+  SELECT id, account_id, invoice_date, target_date, currency, amount_paid, last_payment_attempt
+  FROM invoices
+  WHERE account_id = :accountId
+  ORDER BY invoice_date ASC;
+>>
+
+getInvoicesBySubscription() ::= <<
+  SELECT i.id, i.account_id, i.invoice_date, i.target_date, i.currency, i.amount_paid, i.last_payment_attempt
+  FROM invoices i
+  INNER JOIN invoice_items ii ON i.id = ii.invoice_id
+  WHERE ii.subscription_id = :subscriptionId;
+>>
+
+getInvoicesForPayment() ::= <<
+  SELECT i.id
+  FROM invoices i
+  INNER JOIN invoice_items ii ON i.id = ii.invoice_id
+  WHERE DATEDIFF(:targetDate, i.last_payment_attempt) >= :numberOfDays OR (i.last_payment_attempt IS NULL)
+  GROUP BY i.id, i.amount_paid
+  HAVING SUM(ii.amount) > (i.amount_paid);
+>>
+
+getInvoice() ::= <<
+  SELECT id, account_id, invoice_date, target_date, currency, amount_paid, last_payment_attempt
+  FROM invoices
+  WHERE id = :id;
+>>
+
+createInvoice() ::= <<
+  INSERT INTO invoices(id, account_id, invoice_date, target_date, currency, amount_paid, last_payment_attempt)
+  VALUES (:id, :accountId, :invoiceDate, :targetDate, :currency, :amountPaid, :lastPaymentAttempt);
+>>
+
+notifySuccessfulPayment() ::= <<
+  UPDATE invoices
+  SET amount_paid = amount_paid + :paymentAmount, last_payment_attempt = :paymentDate
+  WHERE id = :id;
+>>
+
+notifyFailedPayment() ::= <<
+  UPDATE invoices
+  SET last_payment_attempt = :paymentAttemptDate
+  WHERE id = :id;
+>>
+
+test() ::= <<
+  SELECT 1
+  FROM invoices;
+>>
+;
\ No newline at end of file
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql b/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
index 1a851a0..963bb53 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
+++ b/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
@@ -1,7 +1,6 @@
 DROP TABLE IF EXISTS invoice_items;
 CREATE TABLE invoice_items (
-  id int(11) unsigned NOT NULL AUTO_INCREMENT,
-  invoice_item_id char(36) NOT NULL,
+  id char(36) NOT NULL,
   invoice_id char(36) NOT NULL,
   subscription_id char(36) NOT NULL,
   start_date datetime NOT NULL,
@@ -9,7 +8,7 @@ CREATE TABLE invoice_items (
   description varchar(100) NOT NULL,
   amount numeric(10,4) NOT NULL,
   rate numeric(10,4) NOT NULL,
-  currency varchar(5) NOT NULL,
+  currency char(3) NOT NULL,
   PRIMARY KEY(id)
 ) ENGINE=innodb;
 
@@ -17,18 +16,32 @@ CREATE INDEX invoice_items_subscription_id ON invoice_items(subscription_id ASC)
 
 DROP TABLE IF EXISTS invoices;
 CREATE TABLE invoices (
-  id int(11) unsigned NOT NULL AUTO_INCREMENT,
-  invoice_id char(36) NOT NULL,
+  id char(36) NOT NULL,
   account_id char(36) NOT NULL,
   invoice_date datetime NOT NULL,
+  target_date datetime NOT NULL,
+  currency char(3) NOT NULL,
   amount_paid numeric(10,4) NOT NULL DEFAULT 0,
-  amount_outstanding numeric(10,4) NOT NULL,
   last_payment_attempt datetime DEFAULT NULL,
   PRIMARY KEY(id)
 ) ENGINE=innodb;
 
 CREATE INDEX invoices_account_id ON invoices(account_id ASC);
-CREATE INDEX invoices_invoice_id ON invoices(invoice_id ASC);
+
+CREATE VIEW amount_remaining AS
+SELECT invoice_items.id,
+       SUM(invoice_items.amount) AS amount_owed
+FROM invoice_items
+GROUP BY invoice_items.id;
+
+CREATE VIEW invoices_for_payment AS
+SELECT i.id,
+       DATEDIFF(NOW(), MAX(i.last_payment_attempt)) AS days_due
+FROM invoices i
+LEFT JOIN amount_remaining ar ON i.id = ar.id
+WHERE (i.amount_paid < ar.amount_owed)
+      AND (i.last_payment_attempt IS NOT NULL)
+GROUP BY i.id;
 
 
 
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 5447094..180919a 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
@@ -16,50 +16,265 @@
 
 package com.ning.billing.invoice.dao;
 
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Stage;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.invoice.api.IInvoice;
+import com.ning.billing.invoice.glue.InjectorMagic;
+import com.ning.billing.invoice.glue.InvoiceModuleMock;
+import com.ning.billing.invoice.model.Invoice;
+import com.ning.billing.invoice.model.InvoiceItem;
+import com.ning.billing.util.clock.Clock;
+import org.apache.commons.io.IOUtils;
+import org.joda.time.DateTime;
+import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
+
+import static org.testng.Assert.*;
+
 @Test(groups = {"invoicing", "invoicing-dao"})
 public class InvoiceDaoTests {
-//    private final MysqlTestingHelper helper = new MysqlTestingHelper();
-//    private InvoiceDao dao;
-//
-//    @BeforeClass(alwaysRun = true)
-//    private void setup() {
-//        final String ddl = IOUtils.toString(InvoiceDao.class.getResourceAsStream("/ddl.sql"));
-//
-//        helper.startMysql();
-//        helper.initDb();
-//
-//        final IDBI dbi = helper.getDBI();
-//        dao = dbi.onDemand(EventDao.class);
-//
-//        // Healthcheck test to make sure MySQL is setup properly
-//        try {
-//            dao.test();
-//        }
-//        catch (Throwable t) {
-//            Assert.fail(t.toString());
-//        }
-//    }
-//
-//    @Test
-//    public void testCreationAndRetrievalByAccount() {
-//        InvoiceDao dao = dbi.onDemand(InvoiceDao.class);
-//        UUID accountId = UUID.randomUUID();
-//        Invoice invoice = new Invoice(accountId, Currency.USD);
-//        DateTime invoiceDate = invoice.getInvoiceDate();
-//
-//        dao.createInvoice(invoice);
-//
-//        List<Invoice> invoices = dao.getInvoicesByAccount(accountId.toString());
-//        assertNotNull(invoices);
-//        assertEquals(invoices.size(), 1);
-//        Invoice thisInvoice = invoices.get(0);
-//        assertEquals(invoice.getAccountId(), accountId);
-//        assertTrue(thisInvoice.getInvoiceDate().equals(invoiceDate));
-//        assertEquals(thisInvoice.getCurrency(), Currency.USD);
-//        assertEquals(thisInvoice.getNumberOfItems(), 0);
-//        assertTrue(thisInvoice.getTotalAmount().compareTo(BigDecimal.ZERO) == 0);
-//    }
+    private final int NUMBER_OF_DAY_BETWEEN_RETRIES = 8;
+
+    private final MysqlTestingHelper helper = new MysqlTestingHelper();
+    private IInvoiceDao dao;
+    private IInvoiceItemDao invoiceItemDao;
+
+    @BeforeClass(alwaysRun = true)
+    private void setup() throws IOException {
+        InvoiceModuleMock module = new InvoiceModuleMock();
+        final String ddl = IOUtils.toString(InvoiceDao.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
+        module.createDb(ddl);
+
+        // Healthcheck test to make sure MySQL is setup properly
+        try {
+            final Injector injector = Guice.createInjector(Stage.DEVELOPMENT, module);
+
+            InjectorMagic injectorMagic = injector.getInstance(InjectorMagic.class);
+            dao = injector.getInstance(InvoiceDao.class);
+            dao.test();
+
+            invoiceItemDao = injector.getInstance(IInvoiceItemDao.class);
+        }
+        catch (Throwable t) {
+            fail(t.toString());
+        }
+    }
+
+    @Test
+    public void testCreationAndRetrievalByAccount() {
+        InvoiceDao dao = InjectorMagic.getInvoiceDao();
+        UUID accountId = UUID.randomUUID();
+        IInvoice invoice = new Invoice(accountId, new Clock().getUTCNow(), Currency.USD);
+        DateTime invoiceDate = invoice.getInvoiceDate();
+
+        dao.createInvoice(invoice);
+
+        List<IInvoice> invoices = dao.getInvoicesByAccount(accountId.toString());
+        assertNotNull(invoices);
+        assertEquals(invoices.size(), 1);
+        IInvoice thisInvoice = invoices.get(0);
+        assertEquals(invoice.getAccountId(), accountId);
+        assertTrue(thisInvoice.getInvoiceDate().compareTo(invoiceDate) == 0);
+        assertEquals(thisInvoice.getCurrency(), Currency.USD);
+        assertEquals(thisInvoice.getNumberOfItems(), 0);
+        assertTrue(thisInvoice.getTotalAmount().compareTo(BigDecimal.ZERO) == 0);
+    }
+
+    @Test
+    public void testRetrievalForNonExistentInvoiceId() {
+        IInvoice invoice = dao.getInvoice(UUID.randomUUID().toString());
+        assertNull(invoice);
+    }
+
+    @Test
+    public void testAddPayment() {
+        UUID accountId = UUID.randomUUID();
+        DateTime targetDate = new DateTime(2011, 10, 6, 0, 0, 0, 0);
+        IInvoice invoice = new Invoice(accountId, targetDate, Currency.USD);
+
+        DateTime paymentAttemptDate = new DateTime(2011, 6, 24, 12, 14, 36, 0);
+        BigDecimal paymentAmount = new BigDecimal("14.0");
+
+        dao.createInvoice(invoice);
+        dao.notifySuccessfulPayment(invoice.getId().toString(), paymentAttemptDate.toDate(), paymentAmount);
+
+        invoice = dao.getInvoice(invoice.getId().toString());
+        assertEquals(invoice.getAmountPaid().compareTo(paymentAmount), 0);
+        assertTrue(invoice.getLastPaymentAttempt().equals(paymentAttemptDate));
+    }
+
+    @Test
+    public void testAddPaymentAttempt() {
+        UUID accountId = UUID.randomUUID();
+        DateTime targetDate = new DateTime(2011, 10, 6, 0, 0, 0, 0);
+        IInvoice invoice = new Invoice(accountId, targetDate, Currency.USD);
+
+        DateTime paymentAttemptDate = new DateTime(2011, 6, 24, 12, 14, 36, 0);
+
+        dao.createInvoice(invoice);
+        dao.notifyFailedPayment(invoice.getId().toString(), paymentAttemptDate.toDate());
+
+        invoice = dao.getInvoice(invoice.getId().toString());
+        assertTrue(invoice.getLastPaymentAttempt().equals(paymentAttemptDate));
+    }
+
+    @Test
+    public void testGetInvoicesForPaymentWithNoResults() {
+        DateTime notionalDate = new DateTime();
+        DateTime targetDate = new DateTime(2011, 10, 6, 0, 0, 0, 0);
+        
+        // determine the number of existing invoices available for payment (to avoid side effects from other tests)
+        List<UUID> invoices = dao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
+        int existingInvoiceCount = invoices.size();
+        
+        UUID accountId = UUID.randomUUID();
+        IInvoice invoice = new Invoice(accountId, targetDate, Currency.USD);
+
+        dao.createInvoice(invoice);
+        invoices = dao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
+        assertEquals(invoices.size(), existingInvoiceCount);
+    }
+
+    // TODO: test invoices for payment with results
+    @Test
+    public void testGetInvoicesForPayment() {
+        List<UUID> invoices;
+        DateTime notionalDate = new DateTime();
+
+        // determine the number of existing invoices available for payment (to avoid side effects from other tests)
+        invoices = dao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
+        int existingInvoiceCount = invoices.size();
+
+        // create a new invoice with one item
+        UUID accountId = UUID.randomUUID();
+        DateTime targetDate = new DateTime(2011, 10, 6, 0, 0, 0, 0);
+        IInvoice invoice = new Invoice(accountId, targetDate, Currency.USD);
+        dao.createInvoice(invoice);
+
+        UUID invoiceId = invoice.getId();
+        UUID subscriptionId = UUID.randomUUID();
+        DateTime endDate = targetDate.plusMonths(3);
+        BigDecimal rate = new BigDecimal("9.0");
+        BigDecimal amount = rate.multiply(new BigDecimal("3.0"));
+
+        InvoiceItem item = new InvoiceItem(invoiceId, subscriptionId, targetDate, endDate, "test", amount, rate, Currency.USD);
+        invoiceItemDao.createInvoiceItem(item);
+
+        // ensure that the number of invoices for payment has increased by 1
+        invoices = dao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
+        assertEquals(invoices.size(), existingInvoiceCount + 1);
+
+        // attempt a payment; ensure that the number of invoices for payment has decreased by 1 (no retries for eight days)
+        dao.notifyFailedPayment(invoice.getId().toString(), notionalDate.toDate());
+        invoices = dao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
+        assertEquals(invoices.size(), existingInvoiceCount);
+
+        // advance clock by 8 days; ensure that number of invoices for payment has increased by 1 (retry)
+        notionalDate = notionalDate.plusDays(NUMBER_OF_DAY_BETWEEN_RETRIES);
+        invoices = dao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
+        assertEquals(invoices.size(), existingInvoiceCount + 1);
+
+        // post successful partial payment; ensure that number of invoices for payment has decreased by 1
+        dao.notifySuccessfulPayment(invoiceId.toString(), notionalDate.toDate(), new BigDecimal("22.0000"));
+        invoices = dao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
+        assertEquals(invoices.size(), existingInvoiceCount);
+
+        // get invoice; verify amount paid is correct
+        invoice = dao.getInvoice(invoiceId.toString());
+        assertEquals(invoice.getAmountPaid().compareTo(new BigDecimal("22.0")), 0);
+
+        // advance clock eight days; ensure that number of invoices for payment has increased by 1 (retry)
+        notionalDate = notionalDate.plusDays(NUMBER_OF_DAY_BETWEEN_RETRIES);
+        invoices = dao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
+        assertEquals(invoices.size(), existingInvoiceCount + 1);
+
+        // post completed payment; ensure that the number of invoices for payment has decreased by 1
+        dao.notifySuccessfulPayment(invoiceId.toString(), notionalDate.toDate(), new BigDecimal("5.0000"));
+        invoices = dao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
+        assertEquals(invoices.size(), existingInvoiceCount);
+
+        // get invoice; verify amount paid is correct
+        invoice = dao.getInvoice(invoiceId.toString());
+        assertEquals(invoice.getAmountPaid().compareTo(new BigDecimal("27.0")), 0);
+
+        // advance clock by 8 days; ensure that the number of invoices for payment hasn't changed
+        notionalDate = notionalDate.plusDays(NUMBER_OF_DAY_BETWEEN_RETRIES);
+        invoices = dao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
+        assertEquals(invoices.size(), existingInvoiceCount);
+
+    }
+
+    @Test
+    public void testGetInvoicesBySubscription() {
+        UUID accountId = UUID.randomUUID();
+
+        UUID subscriptionId1 = UUID.randomUUID(); BigDecimal rate1 = new BigDecimal("17.0");
+        UUID subscriptionId2 = UUID.randomUUID(); BigDecimal rate2 = new BigDecimal("42.0");
+        UUID subscriptionId3 = UUID.randomUUID(); BigDecimal rate3 = new BigDecimal("3.0");
+        UUID subscriptionId4 = UUID.randomUUID(); BigDecimal rate4 = new BigDecimal("12.0");
+
+        DateTime targetDate = new DateTime(2011, 5, 23, 0, 0, 0, 0);
+
+
+        // create invoice 1 (subscriptions 1-4)
+        IInvoice invoice1 = new Invoice(accountId, targetDate, Currency.USD);
+        dao.createInvoice(invoice1);
+
+        UUID invoiceId1 = invoice1.getId();
+
+        DateTime startDate = new DateTime(2011, 3, 1, 0, 0, 0, 0);
+        DateTime endDate = startDate.plusMonths(1);
+
+        InvoiceItem item1 = new InvoiceItem(invoiceId1, subscriptionId1, startDate, endDate, "test A", rate1, rate1, Currency.USD);
+        invoiceItemDao.createInvoiceItem(item1);
+
+        InvoiceItem item2 = new InvoiceItem(invoiceId1, subscriptionId2, startDate, endDate, "test B", rate2, rate2, Currency.USD);
+        invoiceItemDao.createInvoiceItem(item2);
+
+        InvoiceItem item3 = new InvoiceItem(invoiceId1, subscriptionId3, startDate, endDate, "test C", rate3, rate3, Currency.USD);
+        invoiceItemDao.createInvoiceItem(item3);
+
+        InvoiceItem item4 = new InvoiceItem(invoiceId1, subscriptionId4, startDate, endDate, "test D", rate4, rate4, Currency.USD);
+        invoiceItemDao.createInvoiceItem(item4);
+
+        // create invoice 2 (subscriptions 1-3)
+        Invoice invoice = new Invoice(accountId, targetDate, Currency.USD);
+        dao.createInvoice(invoice);
+
+        UUID invoiceId2 = invoice.getId();
+
+        startDate = endDate;
+        endDate = startDate.plusMonths(1);
+
+        InvoiceItem item5 = new InvoiceItem(invoiceId2, subscriptionId1, startDate, endDate, "test A", rate1, rate1, Currency.USD);
+        invoiceItemDao.createInvoiceItem(item5);
+
+        InvoiceItem item6 = new InvoiceItem(invoiceId2, subscriptionId2, startDate, endDate, "test B", rate2, rate2, Currency.USD);
+        invoiceItemDao.createInvoiceItem(item6);
+
+        InvoiceItem item7 = new InvoiceItem(invoiceId2, subscriptionId3, startDate, endDate, "test C", rate3, rate3, Currency.USD);
+        invoiceItemDao.createInvoiceItem(item7);
+
+        // check that each subscription returns the correct number of invoices
+        List<IInvoice> items1 = dao.getInvoicesBySubscription(subscriptionId1.toString());
+        assertEquals(items1.size(), 2);
+
+        List<IInvoice> items2 = dao.getInvoicesBySubscription(subscriptionId2.toString());
+        assertEquals(items2.size(), 2);
+
+        List<IInvoice> items3 = dao.getInvoicesBySubscription(subscriptionId3.toString());
+        assertEquals(items3.size(), 2);
 
+        List<IInvoice> items4 = dao.getInvoicesBySubscription(subscriptionId4.toString());
+        assertEquals(items4.size(), 1);
+    }
+    
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleMock.java b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleMock.java
new file mode 100644
index 0000000..7bccede
--- /dev/null
+++ b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleMock.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.invoice.glue;
+
+import com.ning.billing.dbi.MysqlTestingHelper;
+import org.skife.jdbi.v2.IDBI;
+
+import java.io.IOException;
+
+public class InvoiceModuleMock extends InvoiceModule {
+    private final MysqlTestingHelper helper = new MysqlTestingHelper();
+
+    public void createDb(String ddl) throws IOException {
+        helper.startMysql();
+        helper.initDb(ddl);
+    }
+
+    @Override
+    public void configure() {
+        bind(IDBI.class).toInstance(helper.getDBI());
+        super.configure();
+    }
+}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
index 01a533c..6cf52e4 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
@@ -22,7 +22,12 @@ import com.ning.billing.entitlement.api.billing.BillingMode;
 import com.ning.billing.entitlement.api.billing.IBillingEvent;
 import com.ning.billing.invoice.api.BillingEvent;
 import com.ning.billing.invoice.api.BillingEventSet;
-import com.ning.billing.invoice.model.*;
+import com.ning.billing.invoice.api.IInvoice;
+import com.ning.billing.invoice.api.IInvoiceItem;
+import com.ning.billing.invoice.model.DefaultInvoiceGenerator;
+import com.ning.billing.invoice.model.IInvoiceGenerator;
+import com.ning.billing.invoice.model.InvoiceItem;
+import com.ning.billing.invoice.model.InvoiceItemList;
 import org.joda.time.DateTime;
 import org.testng.annotations.Test;
 
@@ -39,7 +44,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
     @Test
     public void testWithNullEventSetAndNullInvoiceSet() {
         UUID accountId = UUID.randomUUID();
-        Invoice invoice = generator.generateInvoice(accountId, null, null, new DateTime(), Currency.USD);
+        IInvoice invoice = generator.generateInvoice(accountId, null, null, new DateTime(), Currency.USD);
 
         assertNotNull(invoice);
         assertEquals(invoice.getNumberOfItems(), 0);
@@ -52,7 +57,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
 
         InvoiceItemList existingInvoiceItems = new InvoiceItemList();
         UUID accountId = UUID.randomUUID();
-        Invoice invoice = generator.generateInvoice(accountId, events, existingInvoiceItems, new DateTime(), Currency.USD);
+        IInvoice invoice = generator.generateInvoice(accountId, events, existingInvoiceItems, new DateTime(), Currency.USD);
 
         assertNotNull(invoice);
         assertEquals(invoice.getNumberOfItems(), 0);
@@ -77,7 +82,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         
         DateTime targetDate = buildDateTime(2011, 10, 3);
         UUID accountId = UUID.randomUUID();
-        Invoice invoice = generator.generateInvoice(accountId, events, existingInvoiceItems, targetDate, Currency.USD);
+        IInvoice invoice = generator.generateInvoice(accountId, events, existingInvoiceItems, targetDate, Currency.USD);
 
         assertNotNull(invoice);
         assertEquals(invoice.getNumberOfItems(), 1);
@@ -103,7 +108,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         
         DateTime targetDate = buildDateTime(2011, 10, 3);        
         UUID accountId = UUID.randomUUID();
-        Invoice invoice = generator.generateInvoice(accountId, events, existingInvoiceItems, targetDate, Currency.USD);
+        IInvoice invoice = generator.generateInvoice(accountId, events, existingInvoiceItems, targetDate, Currency.USD);
 
         assertNotNull(invoice);
         assertEquals(invoice.getNumberOfItems(), 1);
@@ -133,7 +138,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         InvoiceItemList existingInvoiceItems = new InvoiceItemList();
         DateTime targetDate = buildDateTime(2011, 10, 3);
         UUID accountId = UUID.randomUUID();
-        Invoice invoice = generator.generateInvoice(accountId, events, existingInvoiceItems, targetDate, Currency.USD);
+        IInvoice invoice = generator.generateInvoice(accountId, events, existingInvoiceItems, targetDate, Currency.USD);
 
         assertNotNull(invoice);
         assertEquals(invoice.getNumberOfItems(), 2);
@@ -160,7 +165,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         InvoiceItemList existingInvoiceItems = new InvoiceItemList();
         DateTime targetDate = buildDateTime(2011, 12, 3);
         UUID accountId = UUID.randomUUID();
-        Invoice invoice = generator.generateInvoice(accountId, events, existingInvoiceItems, targetDate, Currency.USD);
+        IInvoice invoice = generator.generateInvoice(accountId, events, existingInvoiceItems, targetDate, Currency.USD);
 
         assertNotNull(invoice);
         assertEquals(invoice.getNumberOfItems(), 2);
@@ -204,7 +209,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         InvoiceItemList existingInvoiceItems = new InvoiceItemList();
         DateTime targetDate = buildDateTime(2011, 12, 3);
         UUID accountId = UUID.randomUUID();
-        Invoice invoice = generator.generateInvoice(accountId, events, existingInvoiceItems, targetDate, Currency.USD);
+        IInvoice invoice = generator.generateInvoice(accountId, events, existingInvoiceItems, targetDate, Currency.USD);
 
         assertNotNull(invoice);
         assertEquals(invoice.getNumberOfItems(), 3);
@@ -226,13 +231,13 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         events.add(event1);
 
         InvoiceItemList existingInvoiceItems = new InvoiceItemList();
-        InvoiceItem invoiceItem = new InvoiceItem(UUID.randomUUID(), subscriptionId, startDate, buildDateTime(2012, 1, 1), "",
+        IInvoiceItem invoiceItem = new InvoiceItem(UUID.randomUUID(), subscriptionId, startDate, buildDateTime(2012, 1, 1), "",
                                                  rate.multiply(FOUR), rate, Currency.USD);
         existingInvoiceItems.add(invoiceItem);
 
         DateTime targetDate = buildDateTime(2011, 12, 3);
         UUID accountId = UUID.randomUUID();
-        Invoice invoice = generator.generateInvoice(accountId, events, existingInvoiceItems, targetDate, Currency.USD);
+        IInvoice invoice = generator.generateInvoice(accountId, events, existingInvoiceItems, targetDate, Currency.USD);
 
         assertNotNull(invoice);
         assertEquals(invoice.getNumberOfItems(), 0);
@@ -410,7 +415,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
     private void testInvoiceGeneration(BillingEventSet events, InvoiceItemList existingInvoiceItems, DateTime targetDate, int expectedNumberOfItems, BigDecimal expectedAmount) {
         Currency currency = Currency.USD;
         UUID accountId = UUID.randomUUID();
-        Invoice invoice = generator.generateInvoice(accountId, events, existingInvoiceItems, targetDate, currency);
+        IInvoice invoice = generator.generateInvoice(accountId, events, existingInvoiceItems, targetDate, currency);
         existingInvoiceItems.addAll(invoice.getItems());
         assertNotNull(invoice);
         assertEquals(invoice.getNumberOfItems(), expectedNumberOfItems);