killbill-uncached

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 7f48c7d..d437ee7 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
@@ -20,6 +20,8 @@ import com.ning.billing.account.dao.IAccountDao;
 import com.ning.billing.account.glue.InjectorMagic;
 import com.ning.billing.catalog.api.Currency;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.UUID;
 
 public class Account extends CustomizableEntityBase implements IAccount {
@@ -32,6 +34,8 @@ public class Account extends CustomizableEntityBase implements IAccount {
     private Currency currency;
     private int billCycleDay;
 
+    private IAccount originalData;
+
     public Account() {
         this(UUID.randomUUID());
     }
@@ -152,12 +156,38 @@ public class Account extends CustomizableEntityBase implements IAccount {
 
     @Override
     protected void loadObject() {
-        IAccount that = dao.getAccountById(id);
-        this.externalKey = that.getExternalKey();
-        this.email = that.getEmail();
-        this.name = that.getName();
-        this.phone = that.getPhone();
-        this.currency = that.getCurrency();
-        this.billCycleDay = that.getBillCycleDay();
+        this.originalData = dao.getAccountById(id);
+        this.externalKey = originalData.getExternalKey();
+        this.email = originalData.getEmail();
+        this.name = originalData.getName();
+        this.phone = originalData.getPhone();
+        this.currency = originalData.getCurrency();
+        this.billCycleDay = originalData.getBillCycleDay();
+    }
+
+    private List<ChangedField> getChangedFields() {
+        List<ChangedField> changedFields = new ArrayList<ChangedField>();
+
+        if (!this.externalKey.equals(originalData.getExternalKey())) {
+            changedFields.add(new ChangedField("externalKey", this.externalKey, originalData.getExternalKey()));
+        }
+        if (!this.email.equals(originalData.getEmail())) {
+            changedFields.add(new ChangedField("email", this.email, originalData.getEmail()));
+        }
+        if (!this.name.equals(originalData.getName())) {
+            changedFields.add(new ChangedField("name", this.name, originalData.getName()));
+        }
+        if (!this.phone.equals(originalData.getPhone())) {
+            changedFields.add(new ChangedField("phone", this.phone, originalData.getPhone()));
+        }
+        if (!this.currency.equals(originalData.getCurrency())) {
+            changedFields.add(new ChangedField("currency", this.currency.toString(), originalData.getCurrency().toString()));
+        }
+        if (this.billCycleDay != originalData.getBillCycleDay()) {
+            changedFields.add(new ChangedField("billCycleDay", Integer.toString(this.billCycleDay),
+                                                               Integer.toString(originalData.getBillCycleDay())));
+        }
+
+        return changedFields;
     }
 }
diff --git a/account/src/main/java/com/ning/billing/account/api/ChangedField.java b/account/src/main/java/com/ning/billing/account/api/ChangedField.java
new file mode 100644
index 0000000..c770106
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/api/ChangedField.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.api;
+
+import com.ning.billing.util.clock.Clock;
+import org.joda.time.DateTime;
+
+public class ChangedField {
+    private final String fieldName;
+    private final String oldValue;
+    private final String newValue;
+    private final DateTime changeDate;
+
+    private final Clock clock = new Clock();
+
+    public ChangedField(String fieldName, String oldValue, String newValue) {
+        this.changeDate = clock.getUTCNow();
+        this.fieldName = fieldName;
+        this.oldValue = oldValue;
+        this.newValue = newValue;
+    }
+}
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
index f5a834b..61d7bd1 100644
--- a/account/src/main/java/com/ning/billing/account/api/EntityCollectionBase.java
+++ b/account/src/main/java/com/ning/billing/account/api/EntityCollectionBase.java
@@ -56,9 +56,8 @@ public abstract class EntityCollectionBase<T extends IEntity> {
         IEntityCollectionDao<T> dao = getCollectionDao();
 
         dao.create(objectId.toString(), objectType, getNewEntities());
-        setEntitiesAsSaved();
-
         dao.update(objectId.toString(), objectType, getUpdatedEntities());
+        setEntitiesAsSaved();
     }
 
     private void setEntitiesAsSaved() {
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
index 40c26fd..edac97f 100644
--- a/account/src/main/java/com/ning/billing/account/api/Tag.java
+++ b/account/src/main/java/com/ning/billing/account/api/Tag.java
@@ -17,6 +17,7 @@
 package com.ning.billing.account.api;
 
 import com.ning.billing.account.dao.ITagDao;
+import org.joda.time.DateTime;
 
 import java.util.UUID;
 
@@ -27,6 +28,8 @@ public class Tag extends EntityBase {
     private String description;
     private UUID objectId;
     private String objectType;
+    private String addedBy;
+    private DateTime dateAdded;
 
     public Tag() {
         super();
@@ -36,6 +39,15 @@ public class Tag extends EntityBase {
         super(id);
     }
 
+    public Tag(UUID id, String description, UUID objectId, String objectType, String addedBy, DateTime dateAdded) {
+        super(id);
+        this.description = description;
+        this.objectId = objectId;
+        this.objectType = objectType;
+        this.addedBy = addedBy;
+        this.dateAdded = dateAdded;
+    }
+
     public UUID getTagDescriptionId() {
         return tagDescriptionId;
     }
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
index d9456bd..dda8884 100644
--- a/account/src/main/java/com/ning/billing/account/api/TagStore.java
+++ b/account/src/main/java/com/ning/billing/account/api/TagStore.java
@@ -35,4 +35,6 @@ public class TagStore extends EntityCollectionBase<Tag> {
     protected IEntityCollectionDao<Tag> getCollectionDao() {
         return InjectorMagic.getTagStoreDao();
     }
-}
+
+
+}
\ No newline at end of file
diff --git a/account/src/main/java/com/ning/billing/account/glue/AccountModule.java b/account/src/main/java/com/ning/billing/account/glue/AccountModule.java
index 24ca0a3..e0b4022 100644
--- a/account/src/main/java/com/ning/billing/account/glue/AccountModule.java
+++ b/account/src/main/java/com/ning/billing/account/glue/AccountModule.java
@@ -36,7 +36,6 @@ public class AccountModule extends AbstractModule {
 
     protected void installAccountDao() {
         bind(IAccountDao.class).to(AccountDao.class).asEagerSingleton();
-//        bind(IAccountDaoSql.class).to(IAccountDaoSql.class).asEagerSingleton();
     }
 
     protected void installAccountUserApi() {
@@ -59,5 +58,4 @@ public class AccountModule extends AbstractModule {
         installAccountService();
         installFieldStore();
     }
-
 }
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 f8fc7a5..3722168 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
@@ -7,8 +7,8 @@ create() ::= <<
 
 update() ::= <<
     UPDATE custom_fields
-    SET object_type = :objectType, object_id = :objectId, field_name = :name, field_value = :value
-    WHERE id = :id;
+    SET field_value = :value
+    WHERE object_type = :objectType AND object_id = :objectId AND field_name = :name;
 >>
 
 load() ::= <<
diff --git a/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java b/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
new file mode 100644
index 0000000..c6adc0a
--- /dev/null
+++ b/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.account.dao;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Stage;
+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 java.io.IOException;
+
+import static org.testng.Assert.fail;
+
+public abstract class AccountDaoTestBase {
+    private InjectorMagic injectorMagic;
+    protected IFieldStoreDao fieldStoreDao;
+    protected IAccountDao accountDao;
+
+    @BeforeClass(alwaysRun = true)
+    protected void setup() throws IOException {
+        // Healthcheck test to make sure MySQL is setup properly
+        try {
+            AccountModuleMock module = new AccountModuleMock();
+            final String ddl = IOUtils.toString(IAccountDaoSql.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
+            module.createDb(ddl);
+
+            final Injector injector = Guice.createInjector(Stage.DEVELOPMENT, module);
+            injectorMagic = injector.getInstance(InjectorMagic.class);
+
+            fieldStoreDao = injector.getInstance(IFieldStoreDao.class);
+            fieldStoreDao.test();
+
+            accountDao = injector.getInstance(IAccountDao.class);
+            accountDao.test();
+
+
+        }
+        catch (Throwable t) {
+            fail(t.toString());
+        }
+    }
+}
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
index 14640a2..c5cc881 100644
--- a/account/src/test/java/com/ning/billing/account/dao/TestFieldStore.java
+++ b/account/src/test/java/com/ning/billing/account/dao/TestFieldStore.java
@@ -16,45 +16,15 @@
 
 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(groups={"account-dao"})
+public class TestFieldStore extends AccountDaoTestBase {
     @Test
     public void testFieldStore() {
         UUID id = UUID.randomUUID();
@@ -72,5 +42,16 @@ public class TestFieldStore {
         fieldStore.load();
 
         assertEquals(fieldStore.getValue(fieldName), fieldValue);
+
+        fieldValue = "Cape Canaveral";
+        fieldStore.setValue(fieldName, fieldValue);
+        assertEquals(fieldStore.getValue(fieldName), fieldValue);
+        fieldStore.save();
+
+        fieldStore = FieldStore.create(id, objectType);
+        assertEquals(fieldStore.getValue(fieldName), null);
+        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 355edbf..05619d3 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
@@ -16,48 +16,18 @@
 
 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.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;
 
-import java.io.IOException;
 import java.util.List;
 import java.util.UUID;
 
 import static org.testng.Assert.*;
 
-@Test(groups = {"Account", "Account-DAO"})
-public class TestSimpleAccountDao {
-    private IAccountDao dao;
-    //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 injectorMagic = injector.getInstance(InjectorMagic.class);
-            dao = injector.getInstance(IAccountDao.class);
-            dao.test();
-        }
-        catch (Throwable t) {
-            fail(t.toString());
-        }
-    }
-
+@Test(groups = {"account-dao"})
+public class TestSimpleAccountDao extends AccountDaoTestBase {
     private final String key = "test1234";
     private final String name = "Wesley";
     private final String email = "dreadpirateroberts@therevenge.com";
@@ -70,22 +40,21 @@ public class TestSimpleAccountDao {
         return account;
     }
 
-    @Test(enabled=true, groups={"Account-DAO"})
     public void testBasic() {
 
         IAccount a = createTestAccount();
-        dao.saveAccount(a);
+        accountDao.saveAccount(a);
         String key = a.getExternalKey();
 
-        IAccount r = dao.getAccountByKey(key);
+        IAccount r = accountDao.getAccountByKey(key);
         assertNotNull(r);
         assertEquals(r.getExternalKey(), a.getExternalKey());
 
-        r = dao.getAccountById(r.getId());
+        r = accountDao.getAccountById(r.getId());
         assertNotNull(r);
         assertEquals(r.getExternalKey(), a.getExternalKey());
 
-        List<IAccount> all = dao.getAccounts();
+        List<IAccount> all = accountDao.getAccounts();
         assertNotNull(all);
         assertTrue(all.size() >= 1);
     }
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
index 631f099..fc32e32 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/IInvoiceService.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/IInvoiceService.java
@@ -20,4 +20,5 @@ import com.ning.billing.lifecycle.IService;
 
 public interface IInvoiceService extends IService {
     public IInvoiceUserApi getInvoiceUserApi();
+    public IInvoicePaymentApi getPaymentApi();
 }
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
index 03202da..582a057 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/IInvoiceItemDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/IInvoiceItemDao.java
@@ -16,27 +16,45 @@
 
 package com.ning.billing.invoice.dao;
 
+import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.IInvoiceItem;
-import com.ning.billing.invoice.model.InvoiceItemList;
+import com.ning.billing.invoice.model.InvoiceItem;
+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.util.List;
+import java.util.UUID;
 
 @ExternalizedSqlViaStringTemplate3()
-@RegisterMapper(InvoiceItemMapper.class)
+@RegisterMapper(IInvoiceItemDao.InvoiceItemMapper.class)
 public interface IInvoiceItemDao {
     @SqlQuery
-    InvoiceItemList getInvoiceItemsByInvoice(@Bind("invoiceId") final String invoiceId);
+    IInvoiceItem getInvoiceItem(@Bind("id") final String invoiceItemId);
 
     @SqlQuery
-    InvoiceItemList getInvoiceItemsByAccount(@Bind("accountId") final String accountId);
+    List<IInvoiceItem> getInvoiceItemsByInvoice(@Bind("invoiceId") final String invoiceId);
+
+    @SqlQuery
+    List<IInvoiceItem> getInvoiceItemsByAccount(@Bind("accountId") final String accountId);
+
+    @SqlQuery
+    List<IInvoiceItem> getInvoiceItemsBySubscription(@Bind("subscriptionId") final String subscriptionId);
 
     @SqlUpdate
     void createInvoiceItem(@InvoiceItemBinder final IInvoiceItem invoiceItem);
 
+    @SqlUpdate
+    void test();
+
     @BindingAnnotation(InvoiceItemBinder.InvoiceItemBinderFactory.class)
     @Retention(RetentionPolicy.RUNTIME)
     @Target({ElementType.PARAMETER})
@@ -59,4 +77,21 @@ public interface IInvoiceItemDao {
             }
         }
     }
+
+    public static class InvoiceItemMapper implements ResultSetMapper<IInvoiceItem> {
+        @Override
+        public IInvoiceItem map(int index, ResultSet result, StatementContext context) throws SQLException {
+            UUID id = UUID.fromString(result.getString("id"));
+            UUID invoiceId = UUID.fromString(result.getString("invoice_id"));
+            UUID subscriptionId = UUID.fromString(result.getString("subscription_id"));
+            DateTime startDate = new DateTime(result.getTimestamp("start_date"));
+            DateTime endDate = new DateTime(result.getTimestamp("end_date"));
+            String description = result.getString("description");
+            BigDecimal amount = result.getBigDecimal("amount");
+            BigDecimal rate = result.getBigDecimal("rate");
+            Currency currency = Currency.valueOf(result.getString("currency"));
+
+            return new InvoiceItem(id, invoiceId, subscriptionId, startDate, endDate, description, amount, rate , currency);
+        }
+    }
 }
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 91c7513..d4dbc29 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
@@ -18,9 +18,10 @@ package com.ning.billing.invoice.dao;
 
 import com.google.inject.Inject;
 import com.ning.billing.invoice.api.IInvoiceItem;
-import com.ning.billing.invoice.model.InvoiceItemList;
 import org.skife.jdbi.v2.IDBI;
 
+import java.util.List;
+
 public class InvoiceItemDao implements IInvoiceItemDao {
     private final IInvoiceItemDao dao;
 
@@ -30,17 +31,32 @@ public class InvoiceItemDao implements IInvoiceItemDao {
     }
 
     @Override
-    public InvoiceItemList getInvoiceItemsByInvoice(String invoiceId) {
+    public IInvoiceItem getInvoiceItem(String invoiceItemId) {
+        return dao.getInvoiceItem(invoiceItemId);
+    }
+
+    @Override
+    public List<IInvoiceItem> getInvoiceItemsByInvoice(String invoiceId) {
         return dao.getInvoiceItemsByInvoice(invoiceId);
     }
 
     @Override
-    public InvoiceItemList getInvoiceItemsByAccount(String accountId) {
+    public List<IInvoiceItem> getInvoiceItemsByAccount(String accountId) {
         return dao.getInvoiceItemsByAccount(accountId);
     }
 
     @Override
+    public List<IInvoiceItem> getInvoiceItemsBySubscription(String subscriptionId) {
+        return dao.getInvoiceItemsBySubscription(subscriptionId);
+    }
+
+    @Override
     public void createInvoiceItem(IInvoiceItem invoiceItem) {
         dao.createInvoiceItem(invoiceItem);
     }
+
+    @Override
+    public void test() {
+        dao.test();
+    }
 }
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 07ff7a1..4ebcf11 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
@@ -35,7 +35,11 @@ public class InvoiceItem implements IInvoiceItem {
     private final Currency currency;
 
     public InvoiceItem(UUID invoiceId, UUID subscriptionId, DateTime startDate, DateTime endDate, String description, BigDecimal amount, BigDecimal rate, Currency currency) {
-        this.id = UUID.randomUUID();
+        this(UUID.randomUUID(), invoiceId, subscriptionId, startDate, endDate, description, amount, rate, currency);
+    }
+
+    public InvoiceItem(UUID id, UUID invoiceId, UUID subscriptionId, DateTime startDate, DateTime endDate, String description, BigDecimal amount, BigDecimal rate, Currency currency) {
+        this.id = id;
         this.invoiceId = invoiceId;
         this.subscriptionId = subscriptionId;
         this.startDate = startDate;
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/IInvoiceItemDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/IInvoiceItemDao.sql.stg
index 9c866cc..ffd4cf9 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/IInvoiceItemDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/IInvoiceItemDao.sql.stg
@@ -1,13 +1,37 @@
 group InvoiceItemDao;
 
+getInvoiceItem() ::= <<
+  SELECT id, invoice_id, subscription_id, start_date, end_date, description, amount, rate, currency
+  FROM invoice_items
+  WHERE id = :id;
+>>
+
 getInvoiceItemsByInvoice() ::= <<
+  SELECT id, invoice_id, subscription_id, start_date, end_date, description, amount, rate, currency
+  FROM invoice_items
+  WHERE invoice_id = :invoiceId;
 >>
 
 getInvoiceItemsByAccount() ::= <<
+  SELECT ii.id, ii.invoice_id, ii.subscription_id, ii.start_date, ii.end_date, ii.description, ii.amount, ii.rate, ii.currency
+  FROM invoice_items ii
+  INNER JOIN invoices i ON i.id = ii.invoice_id
+  WHERE i.account_id = :accountId;
+>>
+
+getInvoiceItemsBySubscription() ::= <<
+  SELECT id, invoice_id, subscription_id, start_date, end_date, description, amount, rate, currency
+  FROM invoice_items
+  WHERE subscription_id = :subscriptionId;
 >>
 
 createInvoiceItem() ::= <<
   INSERT INTO invoice_items(id, invoice_id, subscription_id, start_date, end_date, description, amount, rate, currency)
   VALUES(:id, :invoiceId, :subscriptionId, :startDate, :endDate, :description, :amount, :rate, :currency)
 >>
+
+test() ::= <<
+  SELECT 1
+  FROM invoice_items;
+>>
 ;
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 180919a..906300f 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
@@ -47,14 +47,14 @@ public class InvoiceDaoTests {
     private IInvoiceDao dao;
     private IInvoiceItemDao invoiceItemDao;
 
-    @BeforeClass(alwaysRun = true)
+    @BeforeClass()
     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
+        // Health check test to make sure MySQL is setup properly
         try {
+            InvoiceModuleMock module = new InvoiceModuleMock();
+            final String ddl = IOUtils.toString(InvoiceDao.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
+            module.createDb(ddl);
+
             final Injector injector = Guice.createInjector(Stage.DEVELOPMENT, module);
 
             InjectorMagic injectorMagic = injector.getInstance(InjectorMagic.class);
@@ -62,6 +62,7 @@ public class InvoiceDaoTests {
             dao.test();
 
             invoiceItemDao = injector.getInstance(IInvoiceItemDao.class);
+            invoiceItemDao.test();
         }
         catch (Throwable t) {
             fail(t.toString());
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
new file mode 100644
index 0000000..bd91261
--- /dev/null
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
@@ -0,0 +1,142 @@
+/*
+ * 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.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Stage;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.IInvoiceItem;
+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 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.*;
+
+public class InvoiceItemDaoTests {
+    private InvoiceItemDao dao;
+    private IInvoiceDao invoiceDao;
+
+    @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(InvoiceItemDao.class);
+            dao.test();
+
+            invoiceDao = injector.getInstance(InvoiceDao.class);
+            invoiceDao.test();
+        }
+        catch (Throwable t) {
+            fail(t.toString());
+        }
+    }
+
+    @Test
+    public void testInvoiceItemCreation() {
+        UUID invoiceId = UUID.randomUUID();
+        UUID subscriptionId = UUID.randomUUID();
+        DateTime startDate = new DateTime(2011, 10, 1, 0, 0, 0, 0);
+        DateTime endDate = new DateTime(2011, 11, 1, 0, 0, 0, 0);
+        BigDecimal rate = new BigDecimal("20.00");
+
+        IInvoiceItem item = new InvoiceItem(invoiceId, subscriptionId, startDate, endDate, "test", rate, rate, Currency.USD);
+        dao.createInvoiceItem(item);
+
+        IInvoiceItem thisItem = dao.getInvoiceItem(item.getId().toString());
+        assertNotNull(thisItem);
+        assertEquals(thisItem.getId(), item.getId());
+        assertEquals(thisItem.getInvoiceId(), item.getInvoiceId());
+        assertEquals(thisItem.getSubscriptionId(), item.getSubscriptionId());
+        assertEquals(thisItem.getStartDate(), item.getStartDate());
+        assertEquals(thisItem.getEndDate(), item.getEndDate());
+        assertEquals(thisItem.getDescription(), item.getDescription());
+        assertEquals(thisItem.getAmount().compareTo(item.getAmount()), 0);
+        assertEquals(thisItem.getRate().compareTo(item.getRate()), 0);
+        assertEquals(thisItem.getCurrency(), item.getCurrency());
+    }
+
+    @Test
+    public void testGetInvoiceItemsBySubscriptionId() {
+        UUID subscriptionId = UUID.randomUUID();
+        DateTime startDate = new DateTime(2011, 3, 1, 0, 0, 0, 0);
+        BigDecimal rate = new BigDecimal("20.00");
+
+        for (int i = 0; i < 3; i++) {
+            UUID invoiceId = UUID.randomUUID();
+            InvoiceItem item = new InvoiceItem(invoiceId, subscriptionId, startDate.plusMonths(i), startDate.plusMonths(i + 1), "test", rate, rate, Currency.USD);
+            dao.createInvoiceItem(item);
+        }
+
+        List<IInvoiceItem> items = dao.getInvoiceItemsBySubscription(subscriptionId.toString());
+        assertEquals(items.size(), 3);
+    }
+
+    @Test
+    public void testGetInvoiceItemsByInvoiceId() {
+        UUID invoiceId = UUID.randomUUID();
+        DateTime startDate = new DateTime(2011, 3, 1, 0, 0, 0, 0);
+        BigDecimal rate = new BigDecimal("20.00");
+
+        for (int i = 0; i < 5; i++) {
+            UUID subscriptionId = UUID.randomUUID();
+            BigDecimal amount = rate.multiply(new BigDecimal(i + 1));
+            InvoiceItem item = new InvoiceItem(invoiceId, subscriptionId, startDate, startDate.plusMonths(1), "test", amount, amount, Currency.USD);
+            dao.createInvoiceItem(item);
+        }
+
+        List<IInvoiceItem> items = dao.getInvoiceItemsByInvoice(invoiceId.toString());
+        assertEquals(items.size(), 5);
+    }
+
+    @Test
+    public void testGetInvoiceItemsByAccountId() {
+        UUID accountId = UUID.randomUUID();
+        DateTime targetDate = new DateTime(2011, 5, 23, 0, 0, 0, 0);
+        Invoice invoice = new Invoice(accountId, targetDate, Currency.USD);
+
+        invoiceDao.createInvoice(invoice);
+
+        UUID invoiceId = invoice.getId();
+        DateTime startDate = new DateTime(2011, 3, 1, 0, 0, 0, 0);
+        BigDecimal rate = new BigDecimal("20.00");
+
+        UUID subscriptionId = UUID.randomUUID();
+        InvoiceItem item = new InvoiceItem(invoiceId, subscriptionId, startDate, startDate.plusMonths(1), "test", rate, rate, Currency.USD);
+        dao.createInvoiceItem(item);
+
+        List<IInvoiceItem> items = dao.getInvoiceItemsByAccount(accountId.toString());
+        assertEquals(items.size(), 1);
+    }
+}