killbill-memoizeit

Changes

Details

diff --git a/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverrideDao.java b/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverrideDao.java
index 46b7976..f4ffbc0 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverrideDao.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverrideDao.java
@@ -1,4 +1,27 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    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 org.killbill.billing.catalog.dao;
 
-public class CatalogOverrideDao {
+import org.joda.time.DateTime;
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
+
+public interface CatalogOverrideDao {
+
+    public CatalogOverridePlanDefinitionModelDao getOrCreateOverridePlanDefinition(String parentPlanName, DateTime catalogEffectiveDate, PlanPhasePriceOverride[] resolvedOverride, InternalCallContext context);
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePhaseDefinitionModelDao.java b/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePhaseDefinitionModelDao.java
new file mode 100644
index 0000000..5d73a1c
--- /dev/null
+++ b/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePhaseDefinitionModelDao.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    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 org.killbill.billing.catalog.dao;
+
+import java.math.BigDecimal;
+
+import org.joda.time.DateTime;
+
+public class CatalogOverridePhaseDefinitionModelDao {
+
+    private Long recordId;
+    private String parentPhaseName;
+    private String currency;
+    private BigDecimal fixedPrice;
+    private BigDecimal recurringPrice;
+    private DateTime effectiveDate;
+    private DateTime createdDate;
+    private String createdBy;
+    private Long tenantRecordId;
+
+    public CatalogOverridePhaseDefinitionModelDao() {
+    }
+
+    public CatalogOverridePhaseDefinitionModelDao(final String parentPhaseName, final String currency, final BigDecimal fixedPrice, final BigDecimal recurringPrice, final DateTime effectiveDate) {
+        this.parentPhaseName = parentPhaseName;
+        this.currency = currency;
+        this.fixedPrice = fixedPrice;
+        this.recurringPrice = recurringPrice;
+        this.effectiveDate = effectiveDate;
+    }
+
+    public Long getRecordId() {
+        return recordId;
+    }
+
+    public void setRecordId(final Long recordId) {
+        this.recordId = recordId;
+    }
+
+    public String getParentPhaseName() {
+        return parentPhaseName;
+    }
+
+    public void setParentPhaseName(final String parentPhaseName) {
+        this.parentPhaseName = parentPhaseName;
+    }
+
+    public String getCurrency() {
+        return currency;
+    }
+
+    public void setCurrency(final String currency) {
+        this.currency = currency;
+    }
+
+    public BigDecimal getFixedPrice() {
+        return fixedPrice;
+    }
+
+    public void setFixedPrice(final BigDecimal fixedPrice) {
+        this.fixedPrice = fixedPrice;
+    }
+
+    public BigDecimal getRecurringPrice() {
+        return recurringPrice;
+    }
+
+    public void setRecurringPrice(final BigDecimal recurringPrice) {
+        this.recurringPrice = recurringPrice;
+    }
+
+    public DateTime getEffectiveDate() {
+        return effectiveDate;
+    }
+
+    public void setEffectiveDate(final DateTime effectiveDate) {
+        this.effectiveDate = effectiveDate;
+    }
+
+    public DateTime getCreatedDate() {
+        return createdDate;
+    }
+
+    public void setCreatedDate(final DateTime createdDate) {
+        this.createdDate = createdDate;
+    }
+
+    public String getCreatedBy() {
+        return createdBy;
+    }
+
+    public void setCreatedBy(final String createdBy) {
+        this.createdBy = createdBy;
+    }
+
+    public Long getTenantRecordId() {
+        return tenantRecordId;
+    }
+
+    public void setTenantRecordId(final Long tenantRecordId) {
+        this.tenantRecordId = tenantRecordId;
+    }
+}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePhaseDefinitionSqlDao.java b/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePhaseDefinitionSqlDao.java
index 7cb18e4..eb46a31 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePhaseDefinitionSqlDao.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePhaseDefinitionSqlDao.java
@@ -17,8 +17,14 @@
 
 package org.killbill.billing.catalog.dao;
 
+import java.math.BigDecimal;
+
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.commons.jdbi.binder.SmartBindBean;
 import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.Define;
 import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
 import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
@@ -26,8 +32,23 @@ import org.skife.jdbi.v2.sqlobject.stringtemplate.UseStringTemplate3StatementLoc
 
 @UseStringTemplate3StatementLocator
 public interface CatalogOverridePhaseDefinitionSqlDao extends Transactional<CatalogOverridePhaseDefinitionSqlDao>, CloseMe {
+    @SqlUpdate
+    public void create(@SmartBindBean final CatalogOverridePhaseDefinitionModelDao entity,
+                       @SmartBindBean final InternalCallContext context);
+
+
+    @SqlQuery
+    public CatalogOverridePhaseDefinitionModelDao getByRecordId(@Bind("recordId") final Long recordId,
+                                                               @SmartBindBean final InternalTenantContext context);
+
+    @SqlQuery
+    public CatalogOverridePhaseDefinitionModelDao getByAttributes(@Bind("parentPhaseName") String parentPhaseName,
+                                                                 @Bind("currency") String currency,
+                                                                 @Bind("fixedPrice") BigDecimal fixedPrice,
+                                                                 @Bind("recurringPrice") BigDecimal recurringPrice,
+                                                                 @SmartBindBean final InternalTenantContext context);
 
     @SqlQuery
-    public Long getRecordIdFromObject(@Bind("id") String id, @Define("tableName") final String tableName);
+    public Long getLastInsertId();
 
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePlanDefinitionModelDao.java b/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePlanDefinitionModelDao.java
index bb027ff..6efad3d 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePlanDefinitionModelDao.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePlanDefinitionModelDao.java
@@ -1,4 +1,111 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    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 org.killbill.billing.catalog.dao;
 
+import org.joda.time.DateTime;
+
 public class CatalogOverridePlanDefinitionModelDao {
+
+    private Long recordId;
+    private String parentPlanName;
+    private Boolean isActive;
+    private DateTime effectiveDate;
+    private DateTime createdDate;
+    private String createdBy;
+    private Long tenantRecordId;
+
+    public CatalogOverridePlanDefinitionModelDao() {
+    }
+
+    public CatalogOverridePlanDefinitionModelDao(final String parentPlanName, final Boolean isActive, final DateTime effectiveDate) {
+        this.recordId = 0L;
+        this.parentPlanName = parentPlanName;
+        this.isActive = isActive;
+        this.effectiveDate = effectiveDate;
+    }
+
+    public Long getRecordId() {
+        return recordId;
+    }
+
+    public void setRecordId(final Long recordId) {
+        this.recordId = recordId;
+    }
+
+    public String getParentPlanName() {
+        return parentPlanName;
+    }
+
+    public void setParentPlanName(final String parentPlanName) {
+        this.parentPlanName = parentPlanName;
+    }
+
+    public Boolean getIsActive() {
+        return isActive;
+    }
+
+    public void setIsActive(final Boolean isActive) {
+        this.isActive = isActive;
+    }
+
+    public DateTime getEffectiveDate() {
+        return effectiveDate;
+    }
+
+    public void setEffectiveDate(final DateTime effectiveDate) {
+        this.effectiveDate = effectiveDate;
+    }
+
+    public DateTime getCreatedDate() {
+        return createdDate;
+    }
+
+    public void setCreatedDate(final DateTime createdDate) {
+        this.createdDate = createdDate;
+    }
+
+    public Long getTenantRecordId() {
+        return tenantRecordId;
+    }
+
+    public void setTenantRecordId(final Long tenantRecordId) {
+        this.tenantRecordId = tenantRecordId;
+    }
+
+    public String getCreatedBy() {
+        return createdBy;
+    }
+
+    public void setCreatedBy(final String createdBy) {
+        this.createdBy = createdBy;
+    }
+
+    @Override
+    public String toString() {
+        return "CatalogOverridePlanDefinitionModelDao{" +
+               "recordId=" + recordId +
+               ", parentPlanName='" + parentPlanName + '\'' +
+               ", isActive=" + isActive +
+               ", effectiveDate=" + effectiveDate +
+               ", createdDate=" + createdDate +
+               ", createdBy='" + createdBy + '\'' +
+               ", tenantRecordId=" + tenantRecordId +
+               '}';
+    }
 }
+
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePlanDefinitionSqlDao.java b/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePlanDefinitionSqlDao.java
index f041927..6c73970 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePlanDefinitionSqlDao.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePlanDefinitionSqlDao.java
@@ -17,9 +17,15 @@
 
 package org.killbill.billing.catalog.dao;
 
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.commons.jdbi.binder.SmartBindBean;
+import org.killbill.commons.jdbi.mapper.LowerToCamelBeanMapperFactory;
 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.customizers.Define;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
 import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
 import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.UseStringTemplate3StatementLocator;
@@ -27,7 +33,14 @@ import org.skife.jdbi.v2.sqlobject.stringtemplate.UseStringTemplate3StatementLoc
 @UseStringTemplate3StatementLocator
 public interface CatalogOverridePlanDefinitionSqlDao extends Transactional<CatalogOverridePlanDefinitionSqlDao>, CloseMe {
 
+    @SqlUpdate
+    public void create(@SmartBindBean final CatalogOverridePlanDefinitionModelDao entity,
+                       @SmartBindBean final InternalCallContext context);
+
     @SqlQuery
-    public Long getRecordIdFromObject(@Bind("id") String id, @Define("tableName") final String tableName);
+    public CatalogOverridePlanDefinitionModelDao getByRecordId(@Bind("recordId") final Long recordId,
+                                                               @SmartBindBean final InternalTenantContext context);
 
+    @SqlQuery
+    public Long getLastInsertId();
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePlanPhaseModelDao.java b/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePlanPhaseModelDao.java
new file mode 100644
index 0000000..e4e9c65
--- /dev/null
+++ b/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePlanPhaseModelDao.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    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 org.killbill.billing.catalog.dao;
+
+import org.joda.time.DateTime;
+
+public class CatalogOverridePlanPhaseModelDao {
+
+    private Long recordId;
+    private Short phaseNumber;
+    private Long phaseDefRecordId;
+    private Long targetPlanDefRecordId;
+    private DateTime createdDate;
+    private String createdBy;
+    private Long tenantRecordId;
+
+    public CatalogOverridePlanPhaseModelDao() {
+    }
+
+    public CatalogOverridePlanPhaseModelDao(final Short phaseNumber, final Long phaseDefRecordId, final Long targetPlanDefRecordId) {
+        this.phaseNumber = phaseNumber;
+        this.phaseDefRecordId = phaseDefRecordId;
+        this.targetPlanDefRecordId = targetPlanDefRecordId;
+    }
+
+    public Long getRecordId() {
+        return recordId;
+    }
+
+    public void setRecordId(final Long recordId) {
+        this.recordId = recordId;
+    }
+
+    public Short getPhaseNumber() {
+        return phaseNumber;
+    }
+
+    public void setPhaseNumber(final Short phaseNumber) {
+        this.phaseNumber = phaseNumber;
+    }
+
+    public Long getPhaseDefRecordId() {
+        return phaseDefRecordId;
+    }
+
+    public void setPhaseDefRecordId(final Long phaseDefRecordId) {
+        this.phaseDefRecordId = phaseDefRecordId;
+    }
+
+    public Long getTargetPlanDefRecordId() {
+        return targetPlanDefRecordId;
+    }
+
+    public void setTargetPlanDefRecordId(final Long targetPlanDefRecordId) {
+        this.targetPlanDefRecordId = targetPlanDefRecordId;
+    }
+
+    public DateTime getCreatedDate() {
+        return createdDate;
+    }
+
+    public void setCreatedDate(final DateTime createdDate) {
+        this.createdDate = createdDate;
+    }
+
+    public String getCreatedBy() {
+        return createdBy;
+    }
+
+    public void setCreatedBy(final String createdBy) {
+        this.createdBy = createdBy;
+    }
+
+    public Long getTenantRecordId() {
+        return tenantRecordId;
+    }
+
+    public void setTenantRecordId(final Long tenantRecordId) {
+        this.tenantRecordId = tenantRecordId;
+    }
+}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePlanPhaseSqlDao.java b/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePlanPhaseSqlDao.java
index 60fcfea..e4f0e55 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePlanPhaseSqlDao.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/dao/CatalogOverridePlanPhaseSqlDao.java
@@ -17,9 +17,16 @@
 
 package org.killbill.billing.catalog.dao;
 
+import java.util.Collection;
+import java.util.List;
+
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.util.tag.dao.UUIDCollectionBinder;
+import org.killbill.commons.jdbi.binder.SmartBindBean;
 import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
-import org.skife.jdbi.v2.sqlobject.customizers.Define;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
 import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.UseStringTemplate3StatementLocator;
@@ -27,7 +34,20 @@ import org.skife.jdbi.v2.sqlobject.stringtemplate.UseStringTemplate3StatementLoc
 @UseStringTemplate3StatementLocator
 public interface CatalogOverridePlanPhaseSqlDao extends Transactional<CatalogOverridePlanPhaseSqlDao>, CloseMe {
 
+    @SqlUpdate
+    public void create(@SmartBindBean final CatalogOverridePlanPhaseModelDao entity,
+                       @SmartBindBean final InternalCallContext context);
+
+    @SqlQuery
+    public CatalogOverridePlanPhaseModelDao getByRecordId(@Bind("recordId") final Long recordId,
+                                                          @SmartBindBean final InternalTenantContext context);
+
+    @SqlQuery
+    public Long getTargetPlanDefinition(@PlanPhaseKeysCollectionBinder final Collection<String> concatPhaseNumAndPhaseDefRecordId,
+                                        @Bind("targetCount") final Integer targetCount,
+                                        @SmartBindBean final InternalTenantContext context);
+
     @SqlQuery
-    public Long getRecordIdFromObject(@Bind("id") String id, @Define("tableName") final String tableName);
+    public Long getLastInsertId();
 
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/dao/DefaultCatalogOverrideDao.java b/catalog/src/main/java/org/killbill/billing/catalog/dao/DefaultCatalogOverrideDao.java
index aaf217e..39d4150 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/dao/DefaultCatalogOverrideDao.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/dao/DefaultCatalogOverrideDao.java
@@ -1,4 +1,126 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    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 org.killbill.billing.catalog.dao;
 
-public class DefaultCatalogOverrideDao {
+import java.util.ArrayList;
+import java.util.List;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
+import org.killbill.clock.Clock;
+import org.killbill.commons.jdbi.mapper.LowerToCamelBeanMapperFactory;
+import org.skife.jdbi.v2.DBI;
+import org.skife.jdbi.v2.Handle;
+import org.skife.jdbi.v2.IDBI;
+import org.skife.jdbi.v2.TransactionCallback;
+import org.skife.jdbi.v2.TransactionStatus;
+
+import com.google.inject.Inject;
+
+public class DefaultCatalogOverrideDao implements CatalogOverrideDao {
+
+    private final IDBI dbi;
+    private final Clock clock;
+
+    @Inject
+    public DefaultCatalogOverrideDao(final IDBI dbi, final Clock clock) {
+        this.dbi = dbi;
+        this.clock = clock;
+        // There is no real good place to do that but here (since the sqlDao are NOT EntitySqlDao and DBPProvider belongs in common)... oh well..
+        ((DBI) dbi).registerMapper(new LowerToCamelBeanMapperFactory(CatalogOverridePlanDefinitionModelDao.class));
+        ((DBI) dbi).registerMapper(new LowerToCamelBeanMapperFactory(CatalogOverridePhaseDefinitionModelDao.class));
+        ((DBI) dbi).registerMapper(new LowerToCamelBeanMapperFactory(CatalogOverridePlanPhaseModelDao.class));
+    }
+
+    @Override
+    public CatalogOverridePlanDefinitionModelDao getOrCreateOverridePlanDefinition(final String parentPlanName, final DateTime catalogEffectiveDate, final PlanPhasePriceOverride[] resolvedOverride, final InternalCallContext context) {
+
+        return dbi.inTransaction(new TransactionCallback<CatalogOverridePlanDefinitionModelDao>() {
+            @Override
+            public CatalogOverridePlanDefinitionModelDao inTransaction(final Handle handle, final TransactionStatus status) throws Exception {
+
+                final CatalogOverridePhaseDefinitionModelDao[] overridePhaseDefinitionModelDaos = new CatalogOverridePhaseDefinitionModelDao[resolvedOverride.length];
+                for (int i = 0; i < resolvedOverride.length; i++) {
+                    final PlanPhasePriceOverride curOverride = resolvedOverride[i];
+                    if (curOverride != null) {
+                        final CatalogOverridePhaseDefinitionModelDao createdOverridePhaseDefinitionModelDao = getOrCreateOverridePhaseDefinitionFromTransaction(curOverride.getPhaseName(), catalogEffectiveDate, curOverride, handle, context);
+                        overridePhaseDefinitionModelDaos[i] = createdOverridePhaseDefinitionModelDao;
+                    }
+                }
+
+                final CatalogOverridePlanDefinitionSqlDao sqlDao = handle.attach(CatalogOverridePlanDefinitionSqlDao.class);
+                final Long targetPlanDefinitionRecordId = getOverridePlanDefinitionFromTransaction(overridePhaseDefinitionModelDaos, handle, context);
+                if (targetPlanDefinitionRecordId != null) {
+                    return sqlDao.getByRecordId(targetPlanDefinitionRecordId, context);
+                }
+
+                final CatalogOverridePlanDefinitionModelDao inputPlanDef = new CatalogOverridePlanDefinitionModelDao(parentPlanName, true, catalogEffectiveDate);
+                sqlDao.create(inputPlanDef, context);
+                final Long recordId = sqlDao.getLastInsertId();
+                final CatalogOverridePlanDefinitionModelDao resultPlanDef = sqlDao.getByRecordId(recordId, context);
+
+                for (short i = 0; i < overridePhaseDefinitionModelDaos.length; i++) {
+                    if (overridePhaseDefinitionModelDaos[i] != null) {
+                        createCatalogOverridePlanPhaseFromTransaction(i, overridePhaseDefinitionModelDaos[i], resultPlanDef, handle, context);
+                    }
+                }
+                return resultPlanDef;
+            }
+        });
+    }
+
+
+
+    private Long getOverridePlanDefinitionFromTransaction(final CatalogOverridePhaseDefinitionModelDao[] overridePhaseDefinitionModelDaos, final Handle inTransactionHandle, final InternalCallContext context) {
+        final CatalogOverridePlanPhaseSqlDao sqlDao = inTransactionHandle.attach(CatalogOverridePlanPhaseSqlDao.class);
+
+        final List<String> keys = new ArrayList<String>();
+        for (int i = 0; i < overridePhaseDefinitionModelDaos.length; i++) {
+            final CatalogOverridePhaseDefinitionModelDao cur = overridePhaseDefinitionModelDaos[i];
+            if (cur != null) {
+                // Each key is the concatenation of the phase_number, phase_definition_record_id
+                final StringBuffer key = new StringBuffer();
+                key.append(i);
+                key.append(",");
+                key.append(cur.getRecordId());
+                keys.add(key.toString());
+            }
+        }
+        return sqlDao.getTargetPlanDefinition(keys, keys.size(), context);
+    }
+
+    private void createCatalogOverridePlanPhaseFromTransaction(final short phaseNum, final CatalogOverridePhaseDefinitionModelDao phaseDef, final CatalogOverridePlanDefinitionModelDao planDef, final Handle inTransactionHandle, final InternalCallContext context) {
+        final CatalogOverridePlanPhaseSqlDao sqlDao = inTransactionHandle.attach(CatalogOverridePlanPhaseSqlDao.class);
+        final CatalogOverridePlanPhaseModelDao modelDao = new CatalogOverridePlanPhaseModelDao(phaseNum, phaseDef.getRecordId(), planDef.getRecordId());
+        sqlDao.create(modelDao, context);
+    }
+
+    private CatalogOverridePhaseDefinitionModelDao getOrCreateOverridePhaseDefinitionFromTransaction(final String parentPhaseName, final DateTime catalogEffectiveDate, final PlanPhasePriceOverride override, final Handle inTransactionHandle, final InternalCallContext context) {
+        final CatalogOverridePhaseDefinitionSqlDao sqlDao = inTransactionHandle.attach(CatalogOverridePhaseDefinitionSqlDao.class);
+        CatalogOverridePhaseDefinitionModelDao result = sqlDao.getByAttributes(parentPhaseName, override.getCurrency().name(), override.getFixedPrice(), override.getRecurringPrice(), context);
+        if (result == null) {
+            final CatalogOverridePhaseDefinitionModelDao phaseDef = new CatalogOverridePhaseDefinitionModelDao(parentPhaseName, override.getCurrency().name(), override.getFixedPrice(), override.getRecurringPrice(),
+                                                                                                               catalogEffectiveDate);
+            sqlDao.create(phaseDef, context);
+            final Long recordId = sqlDao.getLastInsertId();
+            result = sqlDao.getByRecordId(recordId, context);
+        }
+        return result;
+    }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/dao/PlanPhaseKeysCollectionBinder.java b/catalog/src/main/java/org/killbill/billing/catalog/dao/PlanPhaseKeysCollectionBinder.java
new file mode 100644
index 0000000..0835ffd
--- /dev/null
+++ b/catalog/src/main/java/org/killbill/billing/catalog/dao/PlanPhaseKeysCollectionBinder.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    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 org.killbill.billing.catalog.dao;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Collection;
+
+import org.killbill.billing.catalog.dao.PlanPhaseKeysCollectionBinder.PlanPhaseKeysCollectionBinderFactory;
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BinderFactory;
+import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
+
+@BindingAnnotation(PlanPhaseKeysCollectionBinderFactory.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.PARAMETER})
+public @interface PlanPhaseKeysCollectionBinder {
+
+    public static class PlanPhaseKeysCollectionBinderFactory implements BinderFactory {
+
+        @Override
+        public Binder build(Annotation annotation) {
+            return new Binder<PlanPhaseKeysCollectionBinder, Collection<String>>() {
+
+                @Override
+                public void bind(SQLStatement<?> query, PlanPhaseKeysCollectionBinder bind, Collection<String> keys) {
+                    query.define("keys", keys);
+
+                    int idx = 0;
+                    for (String state : keys) {
+                        query.bind("key_" + idx, state);
+                        idx++;
+                    }
+                }
+            };
+        }
+    }
+}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlanPhasePriceOverride.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlanPhasePriceOverride.java
index 279d49b..2a1ee1c 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlanPhasePriceOverride.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlanPhasePriceOverride.java
@@ -31,14 +31,22 @@ public class DefaultPlanPhasePriceOverride implements PlanPhasePriceOverride {
     private final BigDecimal fixedPrice;
     private final BigDecimal recurringPrice;
 
-    public DefaultPlanPhasePriceOverride(final String phaseName, final PlanPhaseSpecifier planPhaseSpecifier, final Currency currency, final BigDecimal fixedPrice, final BigDecimal recurringPrice) {
-        this.phaseName = phaseName;
+    public DefaultPlanPhasePriceOverride(final PlanPhaseSpecifier planPhaseSpecifier, final Currency currency, final BigDecimal fixedPrice, final BigDecimal recurringPrice) {
+        this.phaseName = null;
         this.planPhaseSpecifier = planPhaseSpecifier;
         this.currency = currency;
         this.fixedPrice = fixedPrice;
         this.recurringPrice = recurringPrice;
     }
 
+    public DefaultPlanPhasePriceOverride(final String phaseName, final Currency currency, final BigDecimal fixedPrice, final BigDecimal recurringPrice) {
+        this.phaseName = phaseName;
+        this.planPhaseSpecifier = null;
+        this.currency = currency;
+        this.fixedPrice = fixedPrice;
+        this.recurringPrice = recurringPrice;
+    }
+
     @Override
     public String getPhaseName() {
         return phaseName;
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/glue/CatalogModule.java b/catalog/src/main/java/org/killbill/billing/catalog/glue/CatalogModule.java
index 7118e5f..32685e9 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/glue/CatalogModule.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/glue/CatalogModule.java
@@ -25,8 +25,12 @@ import org.killbill.billing.catalog.api.user.DefaultCatalogUserApi;
 import org.killbill.billing.catalog.caching.CatalogCache;
 import org.killbill.billing.catalog.caching.CatalogCacheInvalidationCallback;
 import org.killbill.billing.catalog.caching.EhCacheCatalogCache;
+import org.killbill.billing.catalog.dao.CatalogOverrideDao;
+import org.killbill.billing.catalog.dao.DefaultCatalogOverrideDao;
 import org.killbill.billing.catalog.io.CatalogLoader;
 import org.killbill.billing.catalog.io.VersionedCatalogLoader;
+import org.killbill.billing.catalog.override.DefaultPriceOverride;
+import org.killbill.billing.catalog.override.PriceOverride;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
 import org.killbill.billing.util.config.CatalogConfig;
@@ -51,6 +55,11 @@ public class CatalogModule extends KillBillModule {
     protected void installCatalog() {
         bind(CatalogService.class).to(DefaultCatalogService.class).asEagerSingleton();
         bind(CatalogLoader.class).to(VersionedCatalogLoader.class).asEagerSingleton();
+        bind(PriceOverride.class).to(DefaultPriceOverride.class).asEagerSingleton();
+    }
+
+    protected void installCatalogDao() {
+        bind(CatalogOverrideDao.class).to(DefaultCatalogOverrideDao.class).asEagerSingleton();
     }
 
     protected void installCatalogUserApi() {
@@ -62,9 +71,12 @@ public class CatalogModule extends KillBillModule {
         bind(CacheInvalidationCallback.class).annotatedWith(Names.named(CATALOG_INVALIDATION_CALLBACK)).to(CatalogCacheInvalidationCallback.class).asEagerSingleton();
     }
 
+
+
     @Override
     protected void configure() {
         installConfig();
+        installCatalogDao();
         installCatalog();
         installCatalogUserApi();
         installCatalogConfigCache();
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java b/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
index 460b9a6..66501d1 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
@@ -31,19 +31,24 @@ import org.killbill.billing.ErrorCode;
 import org.killbill.billing.catalog.StandaloneCatalog;
 import org.killbill.billing.catalog.VersionedCatalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.catalog.override.PriceOverride;
 import org.killbill.billing.platform.api.KillbillService.ServiceException;
 import org.killbill.clock.Clock;
 import org.killbill.xmlloader.UriAccessor;
 import org.killbill.xmlloader.XMLLoader;
 
 public class VersionedCatalogLoader implements CatalogLoader {
+
     private static final Object PROTOCOL_FOR_FILE = "file";
-    private final String XML_EXTENSION = ".xml";
+    private static final String XML_EXTENSION = ".xml";
+
     private final Clock clock;
+    private final PriceOverride priceOverride;
 
     @Inject
-    public VersionedCatalogLoader(final Clock clock) {
+    public VersionedCatalogLoader(final Clock clock, final PriceOverride priceOverride) {
         this.clock = clock;
+        this.priceOverride = priceOverride;
     }
 
     /* (non-Javadoc)
@@ -52,8 +57,7 @@ public class VersionedCatalogLoader implements CatalogLoader {
     @Override
     public VersionedCatalog load(final String uriString) throws CatalogApiException {
         try {
-            List<URI> xmlURIs = null;
-
+            List<URI> xmlURIs;
             if (uriString.endsWith(XML_EXTENSION)) { // Assume its an xml file
                 xmlURIs = new ArrayList<URI>();
                 URI uri = new URI(uriString);
@@ -94,6 +98,7 @@ public class VersionedCatalogLoader implements CatalogLoader {
             for (final String cur : catalogXMLs) {
                 final InputStream curCatalogStream = new ByteArrayInputStream(cur.getBytes());
                 final StandaloneCatalog catalog = XMLLoader.getObjectFromStream(uri, curCatalogStream, StandaloneCatalog.class);
+                catalog.setPriceOverride(priceOverride);
                 result.add(catalog);
             }
             return result;
@@ -179,6 +184,4 @@ public class VersionedCatalogLoader implements CatalogLoader {
         }
         return new URI(url.toString() + f);
     }
-
-
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/override/DefaultPriceOverride.java b/catalog/src/main/java/org/killbill/billing/catalog/override/DefaultPriceOverride.java
index 792a609..c5415f2 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/override/DefaultPriceOverride.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/override/DefaultPriceOverride.java
@@ -1,4 +1,39 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    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 org.killbill.billing.catalog.override;
 
-public class DefaultPriceOverride {
+import java.util.List;
+
+import org.killbill.billing.catalog.DefaultPlan;
+import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.catalog.api.Plan;
+import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
+
+import com.google.inject.Inject;
+
+public class DefaultPriceOverride implements PriceOverride {
+
+    @Inject
+    public DefaultPriceOverride() {
+    }
+
+    @Override
+    public DefaultPlan getOverriddenPlan(final Plan parentPlan, final List<PlanPhasePriceOverride> overrides) throws CatalogApiException {
+        return null;
+    }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/override/PriceOverride.java b/catalog/src/main/java/org/killbill/billing/catalog/override/PriceOverride.java
index e099a37..5303b8d 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/override/PriceOverride.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/override/PriceOverride.java
@@ -1,4 +1,29 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    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 org.killbill.billing.catalog.override;
 
+import java.util.List;
+
+import org.killbill.billing.catalog.DefaultPlan;
+import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.catalog.api.Plan;
+import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
+
 public interface PriceOverride {
+    DefaultPlan getOverriddenPlan(Plan parentPlan, List<PlanPhasePriceOverride> overrides) throws CatalogApiException;
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
index bc035fb..399db49 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
@@ -48,6 +48,7 @@ import org.killbill.billing.catalog.api.PriceList;
 import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.catalog.api.StaticCatalog;
+import org.killbill.billing.catalog.override.PriceOverride;
 import org.killbill.billing.catalog.rules.PlanRules;
 import org.killbill.xmlloader.ValidatingConfig;
 import org.killbill.xmlloader.ValidationErrors;
@@ -66,8 +67,6 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
     @XmlElement(required = true)
     private BillingMode recurringBillingMode;
 
-    private URI catalogURI;
-
     @XmlElementWrapper(name = "currencies", required = true)
     @XmlElement(name = "currency", required = true)
     private Currency[] supportedCurrencies;
@@ -90,6 +89,11 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
     @XmlElement(name = "priceLists", required = true)
     private DefaultPriceListSet priceLists;
 
+
+    private URI catalogURI;
+
+    private PriceOverride priceOverride;
+
     public StandaloneCatalog() {
     }
 
@@ -161,7 +165,7 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
       * @see org.killbill.billing.catalog.ICatalog#getPlan(java.lang.String, java.lang.String)
       */
     @Override
-    public DefaultPlan findCurrentPlan(final String productName, final BillingPeriod period, final String priceListName, List<PlanPhasePriceOverride> overrides) throws CatalogApiException {
+    public DefaultPlan findCurrentPlan(final String productName, final BillingPeriod period, final String priceListName, final List<PlanPhasePriceOverride> overrides) throws CatalogApiException {
         if (productName == null) {
             throw new CatalogApiException(ErrorCode.CAT_NULL_PRODUCT_NAME);
         }
@@ -174,7 +178,11 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
             final String periodString = (period == null) ? "NULL" : period.toString();
             throw new CatalogApiException(ErrorCode.CAT_PLAN_NOT_FOUND, productName, periodString, priceListName);
         }
-        return result;
+        if (overrides != null && !overrides.isEmpty()) {
+            return priceOverride.getOverriddenPlan(result, overrides);
+        } else {
+            return  result;
+        }
     }
 
     @Override
@@ -344,6 +352,10 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
         return this;
     }
 
+    public void setPriceOverride(final PriceOverride priceOverride) {
+        this.priceOverride = priceOverride;
+    }
+
     @Override
     public boolean canCreatePlan(final PlanSpecifier specifier) throws CatalogApiException {
         final Product product = findCurrentProduct(specifier.getProductName());
@@ -398,7 +410,6 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
                 }
             }
         }
-
         return availBasePlans;
     }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
index ed6b525..2bfb1ec 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
@@ -58,8 +58,6 @@ import org.killbill.clock.Clock;
 import org.killbill.xmlloader.ValidatingConfig;
 import org.killbill.xmlloader.ValidationErrors;
 
-import com.google.common.collect.ImmutableList;
-
 @XmlRootElement(name = "catalog")
 @XmlAccessorType(XmlAccessType.NONE)
 public class VersionedCatalog extends ValidatingConfig<StandaloneCatalog> implements Catalog, StaticCatalog {
@@ -120,11 +118,6 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalog> implem
         }
 
         public PlanRequestWrapper(final String productName, final BillingPeriod bp,
-                                  final String priceListName) {
-            this(productName, bp, priceListName, ImmutableList.<PlanPhasePriceOverride>of());
-        }
-
-        public PlanRequestWrapper(final String productName, final BillingPeriod bp,
                                   final String priceListName, List<PlanPhasePriceOverride> overrides) {
             this.productName = productName;
             this.bp = bp;
@@ -141,7 +134,6 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalog> implem
         }
     }
 
-    // STEPH_PO implement catalog logic...
     private Plan findPlan(final PlanRequestWrapper wrapper,
                           final DateTime requestedDate,
                           final DateTime subscriptionStartDate)
@@ -153,7 +145,7 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalog> implem
 
         for (int i = catalogs.size() - 1; i >= 0; i--) { // Working backwards to find the latest applicable plan
             final StandaloneCatalog c = catalogs.get(i);
-            Plan plan = null;
+            Plan plan;
             try {
                 plan = wrapper.findPlan(c);
             } catch (CatalogApiException e) {
@@ -275,7 +267,7 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalog> implem
                          final DateTime requestedDate,
                          final DateTime subscriptionStartDate)
             throws CatalogApiException {
-        return findPlan(new PlanRequestWrapper(productName, term, priceListName), requestedDate, subscriptionStartDate);
+        return findPlan(new PlanRequestWrapper(productName, term, priceListName, overrides), requestedDate, subscriptionStartDate);
     }
 
     //
diff --git a/catalog/src/main/resources/org/killbill/billing/catalog/dao/CatalogOverridePhaseDefinitionSqlDao.sql.stg b/catalog/src/main/resources/org/killbill/billing/catalog/dao/CatalogOverridePhaseDefinitionSqlDao.sql.stg
new file mode 100644
index 0000000..554f8d2
--- /dev/null
+++ b/catalog/src/main/resources/org/killbill/billing/catalog/dao/CatalogOverridePhaseDefinitionSqlDao.sql.stg
@@ -0,0 +1,73 @@
+group CatalogOverridePhaseDefinitionSqlDao;
+
+tableName() ::= "catalog_override_phase_definition"
+
+
+tableFields(prefix) ::= <<
+  <prefix>parent_phase_name
+, <prefix>currency
+, <prefix>fixed_price
+, <prefix>recurring_price
+, <prefix>effective_date
+, <prefix>created_date
+, <prefix>created_by
+, <prefix>tenant_record_id
+>>
+
+allTableFields(prefix) ::= <<
+  <prefix>record_id
+, <tableFields(prefix)>
+>>
+
+
+tableValues() ::= <<
+  :parentPhaseName
+, :currency
+, :fixedPrice
+, :recurringPrice
+, :effectiveDate
+, :createdDate
+, :createdBy
+, :tenantRecordId
+>>
+
+
+allTableValues() ::= <<
+  :recordId
+, <tableValues()>
+>>
+
+create() ::= <<
+insert into <tableName()> (
+<tableFields()>
+)
+values (
+<tableValues()>
+)
+;
+>>
+
+getByRecordId() ::= <<
+select <allTableFields()>
+from <tableName()>
+where record_id = :recordId
+and tenant_record_id = :tenantRecordId
+;
+>>
+
+getByAttributes() ::= <<
+select <allTableFields()>
+from <tableName()>
+where parent_phase_name = :parentPhaseName
+and currency = :currency
+and (fixed_price = :fixedPrice or (fixed_price is null and :fixedPrice is null))
+and (recurring_price = :recurringPrice or (recurring_price is null and :recurringPrice is null))
+and tenant_record_id = :tenantRecordId
+;
+>>
+
+
+getLastInsertId() ::= <<
+select LAST_INSERT_ID();
+>>
+
diff --git a/catalog/src/main/resources/org/killbill/billing/catalog/dao/CatalogOverridePlanDefinitionSqlDao.sql.stg b/catalog/src/main/resources/org/killbill/billing/catalog/dao/CatalogOverridePlanDefinitionSqlDao.sql.stg
index e69de29..3024bc0 100644
--- a/catalog/src/main/resources/org/killbill/billing/catalog/dao/CatalogOverridePlanDefinitionSqlDao.sql.stg
+++ b/catalog/src/main/resources/org/killbill/billing/catalog/dao/CatalogOverridePlanDefinitionSqlDao.sql.stg
@@ -0,0 +1,56 @@
+group CatalogOverridePlanDefinitionSqlDao;
+
+tableName() ::= "catalog_override_plan_definition"
+
+tableFields(prefix) ::= <<
+  <prefix>parent_plan_name
+, <prefix>effective_date
+, <prefix>is_active
+, <prefix>created_date
+, <prefix>created_by
+, <prefix>tenant_record_id
+>>
+
+allTableFields(prefix) ::= <<
+  <prefix>record_id
+, <tableFields(prefix)>
+>>
+
+
+tableValues() ::= <<
+  :parentPlanName
+, :effectiveDate
+, :isActive
+, :createdDate
+, :createdBy
+, :tenantRecordId
+>>
+
+
+allTableValues() ::= <<
+  :recordId
+, <tableValues()>
+>>
+
+create() ::= <<
+insert into <tableName()> (
+<tableFields()>
+)
+values (
+<tableValues()>
+)
+;
+>>
+
+getByRecordId() ::= <<
+select <allTableFields()>
+from <tableName()>
+where record_id = :recordId
+and tenant_record_id = :tenantRecordId
+;
+>>
+
+getLastInsertId() ::= <<
+    select LAST_INSERT_ID();
+>>
+
diff --git a/catalog/src/main/resources/org/killbill/billing/catalog/dao/CatalogOverridePlanPhaseSqlDao.sql.stg b/catalog/src/main/resources/org/killbill/billing/catalog/dao/CatalogOverridePlanPhaseSqlDao.sql.stg
new file mode 100644
index 0000000..356eea2
--- /dev/null
+++ b/catalog/src/main/resources/org/killbill/billing/catalog/dao/CatalogOverridePlanPhaseSqlDao.sql.stg
@@ -0,0 +1,78 @@
+group CatalogOverridePlanPhaseSqlDao;
+
+
+tableName() ::= "catalog_override_plan_phase"
+
+
+tableFields(prefix) ::= <<
+  <prefix>phase_number
+, <prefix>phase_def_record_id
+, <prefix>target_plan_def_record_id
+, <prefix>created_date
+, <prefix>created_by
+, <prefix>tenant_record_id
+>>
+
+allTableFields(prefix) ::= <<
+  <prefix>record_id
+, <tableFields(prefix)>
+>>
+
+
+tableValues() ::= <<
+  :phaseNumber
+, :phaseDefRecordId
+, :targetPlanDefRecordId
+, :createdDate
+, :createdBy
+, :tenantRecordId
+>>
+
+
+allTableValues() ::= <<
+  :recordId
+, <tableValues()>
+>>
+
+create() ::= <<
+insert into <tableName()> (
+<tableFields()>
+)
+values (
+<tableValues()>
+)
+;
+>>
+
+getByRecordId() ::= <<
+select <allTableFields()>
+from
+<tableName()>
+where record_id = :recordId
+and tenant_record_id = :tenantRecordId
+;
+>>
+
+getTargetPlanDefinition(keys) ::= <<
+select
+target_plan_def_record_id
+from (select
+      target_plan_def_record_id
+      , count(*) count
+      from
+      <tableName()>
+      where
+      concat_ws(',', phase_number, phase_def_record_id) in (<keys: {key | :key_<i0>}; separator="," >)
+      and tenant_record_id = :tenantRecordId
+      group by 1) tmp
+where
+1=1
+and tmp.count = :targetCount
+;
+>>
+
+
+getLastInsertId() ::= <<
+select LAST_INSERT_ID();
+>>
+
diff --git a/catalog/src/main/resources/org/killbill/billing/catalog/ddl.sql b/catalog/src/main/resources/org/killbill/billing/catalog/ddl.sql
index e69de29..0bc969c 100644
--- a/catalog/src/main/resources/org/killbill/billing/catalog/ddl.sql
+++ b/catalog/src/main/resources/org/killbill/billing/catalog/ddl.sql
@@ -0,0 +1,43 @@
+/*! SET storage_engine=INNODB */;
+
+DROP TABLE IF EXISTS catalog_override_plan_definition;
+CREATE TABLE catalog_override_plan_definition (
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    parent_plan_name varchar(255) NOT NULL,
+    effective_date datetime NOT NULL,
+    is_active bool DEFAULT 1,
+    created_date datetime NOT NULL,
+    created_by varchar(50) NOT NULL,
+    tenant_record_id int(11) unsigned default null,
+    PRIMARY KEY(record_id)
+) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
+CREATE INDEX catalog_override_plan_definition_tenant_record_id ON catalog_override_plan_definition(tenant_record_id);
+
+
+DROP TABLE IF EXISTS catalog_override_phase_definition;
+CREATE TABLE catalog_override_phase_definition (
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    parent_phase_name varchar(255) NOT NULL,
+    currency char(3) NOT NULL,
+    fixed_price numeric(15,9) NULL,
+    recurring_price numeric(15,9) NULL,
+    effective_date datetime NOT NULL,
+    created_date datetime NOT NULL,
+    created_by varchar(50) NOT NULL,
+    tenant_record_id int(11) unsigned default null,
+    PRIMARY KEY(record_id)
+) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
+CREATE INDEX catalog_override_phase_definition_idx ON catalog_override_phase_definition(tenant_record_id, parent_phase_name, currency);
+
+DROP TABLE IF EXISTS catalog_override_plan_phase;
+CREATE TABLE catalog_override_plan_phase (
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    phase_number tinyint(3) unsigned NOT NULL,
+    phase_def_record_id int(11) unsigned NOT NULL,
+    target_plan_def_record_id int(11) unsigned NOT NULL,
+    created_date datetime NOT NULL,
+    created_by varchar(50) NOT NULL,
+    tenant_record_id int(11) unsigned default null,
+    PRIMARY KEY(record_id)
+) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
+CREATE INDEX catalog_override_plan_phase_idx ON catalog_override_plan_phase(tenant_record_id, phase_number, phase_def_record_id);
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteWithEmbeddedDB.java b/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteWithEmbeddedDB.java
index 2c8c754..0cbebac 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteWithEmbeddedDB.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteWithEmbeddedDB.java
@@ -1,4 +1,50 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    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 org.killbill.billing.catalog;
 
-public class CatalogTestSuiteWithEmbeddedDB {
+import org.killbill.billing.GuicyKillbillTestSuiteWithEmbeddedDB;
+import org.killbill.billing.catalog.dao.CatalogOverrideDao;
+import org.killbill.billing.catalog.glue.TestCatalogModuleWithEmbeddedDB;
+import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.skife.jdbi.v2.IDBI;
+import org.testng.annotations.BeforeClass;
+
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+
+public class CatalogTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWithEmbeddedDB {
+
+    @Inject
+    protected CatalogOverrideDao catalogOverrideDao;
+
+    @Inject
+    protected IDBI dbi;
+
+    @Override
+    protected KillbillConfigSource getConfigSource() {
+        return getConfigSource("/resource.properties");
+    }
+
+    @BeforeClass(groups = "slow")
+    protected void beforeClass() throws Exception {
+        final Injector injector = Guice.createInjector(new TestCatalogModuleWithEmbeddedDB(configSource));
+        injector.injectMembers(this);
+    }
+
 }
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/dao/TestCatalogOverrideDao.java b/catalog/src/test/java/org/killbill/billing/catalog/dao/TestCatalogOverrideDao.java
index c96835b..7f96efb 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/dao/TestCatalogOverrideDao.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/dao/TestCatalogOverrideDao.java
@@ -1,4 +1,71 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    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 org.killbill.billing.catalog.dao;
 
-public class TestCatalogOverrideDao {
+import java.math.BigDecimal;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.catalog.CatalogTestSuiteWithEmbeddedDB;
+import org.killbill.billing.catalog.DefaultPlanPhasePriceOverride;
+import org.killbill.billing.catalog.StandaloneCatalog;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.Plan;
+import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
+import org.killbill.xmlloader.XMLLoader;
+import org.skife.jdbi.v2.Handle;
+import org.skife.jdbi.v2.tweak.HandleCallback;
+import org.testng.annotations.Test;
+
+import com.google.common.io.Resources;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+public class TestCatalogOverrideDao extends CatalogTestSuiteWithEmbeddedDB {
+
+    @Test(groups = "slow")
+    public void testOverrideLastPhase() throws Exception {
+
+        final StandaloneCatalog catalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarBasic.xml").toExternalForm(), StandaloneCatalog.class);
+        final Plan plan = catalog.findCurrentPlan("standard-monthly");
+
+        final PlanPhasePriceOverride[] resolvedOverrides = new PlanPhasePriceOverride[plan.getAllPhases().length];
+        resolvedOverrides[0] = null;
+        resolvedOverrides[1] = new DefaultPlanPhasePriceOverride(plan.getFinalPhase().getName(), Currency.USD, null, new BigDecimal("128.76"));
+        final CatalogOverridePlanDefinitionModelDao newPlan = catalogOverrideDao.getOrCreateOverridePlanDefinition(plan.getName(), new DateTime(catalog.getEffectiveDate()), resolvedOverrides, internalCallContext);
+        assertEquals(newPlan.getParentPlanName(), "standard-monthly");
+        assertTrue(newPlan.getIsActive());
+    }
+
+    @Test(groups = "slow")
+    public void testOverrideTwoOutOfThreePhases() throws Exception {
+
+        final StandaloneCatalog catalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarAdvanced.xml").toExternalForm(), StandaloneCatalog.class);
+        final Plan plan = catalog.findCurrentPlan("discount-standard-monthly");
+
+        final PlanPhasePriceOverride[] resolvedOverrides = new PlanPhasePriceOverride[plan.getAllPhases().length];
+        resolvedOverrides[0] = new DefaultPlanPhasePriceOverride(plan.getAllPhases()[0].getName(), Currency.USD, BigDecimal.TEN, null);
+        resolvedOverrides[1] = null;
+        resolvedOverrides[2] = new DefaultPlanPhasePriceOverride(plan.getFinalPhase().getName(), Currency.USD, null, new BigDecimal("348.64"));
+        final CatalogOverridePlanDefinitionModelDao newPlan = catalogOverrideDao.getOrCreateOverridePlanDefinition(plan.getName(), new DateTime(catalog.getEffectiveDate()), resolvedOverrides, internalCallContext);
+        assertEquals(newPlan.getParentPlanName(), "discount-standard-monthly");
+        assertTrue(newPlan.getIsActive());
+
+    }
 }
+
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/dao/TestCatalogOverridePhaseDefinitionSqlDao.java b/catalog/src/test/java/org/killbill/billing/catalog/dao/TestCatalogOverridePhaseDefinitionSqlDao.java
new file mode 100644
index 0000000..aa2dc48
--- /dev/null
+++ b/catalog/src/test/java/org/killbill/billing/catalog/dao/TestCatalogOverridePhaseDefinitionSqlDao.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    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 org.killbill.billing.catalog.dao;
+
+import java.math.BigDecimal;
+
+import org.killbill.billing.catalog.CatalogTestSuiteWithEmbeddedDB;
+import org.killbill.commons.jdbi.mapper.LowerToCamelBeanMapperFactory;
+import org.skife.jdbi.v2.DBI;
+import org.skife.jdbi.v2.Handle;
+import org.skife.jdbi.v2.TransactionCallback;
+import org.skife.jdbi.v2.TransactionStatus;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+public class TestCatalogOverridePhaseDefinitionSqlDao extends CatalogTestSuiteWithEmbeddedDB {
+
+    @BeforeClass(groups = "slow")
+    public void beforeClass() throws Exception {
+        super.beforeClass();
+        ((DBI) dbi).registerMapper(new LowerToCamelBeanMapperFactory(CatalogOverridePhaseDefinitionModelDao.class));
+    }
+
+    @Test(groups = "slow")
+    public void testBasic() throws Exception {
+
+        final CatalogOverridePhaseDefinitionModelDao obj1 = new CatalogOverridePhaseDefinitionModelDao("p1", "EUR", BigDecimal.ONE, BigDecimal.TEN, clock.getUTCNow());
+
+        performTestInTransaction(new WithCatalogOverridePhaseDefinitionSqlDaoTransaction<Void>() {
+            @Override
+            public Void doTransaction(final CatalogOverridePhaseDefinitionSqlDao sqlDao) {
+                sqlDao.create(obj1, internalCallContext);
+                final Long lastInserted = sqlDao.getLastInsertId();
+
+                final CatalogOverridePhaseDefinitionModelDao rehydrated = sqlDao.getByRecordId(lastInserted, internalCallContext);
+                assertEquals(rehydrated.getParentPhaseName(), obj1.getParentPhaseName());
+                assertEquals(rehydrated.getFixedPrice().compareTo(obj1.getFixedPrice()), 0);
+                assertEquals(rehydrated.getRecurringPrice().compareTo(obj1.getRecurringPrice()), 0);
+                assertEquals(rehydrated.getCurrency(), obj1.getCurrency());
+                return null;
+            }
+        });
+    }
+
+    @Test(groups = "slow")
+    public void testBasicWithNullPrices() throws Exception {
+
+        final CatalogOverridePhaseDefinitionModelDao obj1 = new CatalogOverridePhaseDefinitionModelDao("p2", "USD", null, new BigDecimal("54.21"), clock.getUTCNow());
+
+        performTestInTransaction(new WithCatalogOverridePhaseDefinitionSqlDaoTransaction<Void>() {
+            @Override
+            public Void doTransaction(final CatalogOverridePhaseDefinitionSqlDao sqlDao) {
+                sqlDao.create(obj1, internalCallContext);
+                final Long lastInserted = sqlDao.getLastInsertId();
+
+                final CatalogOverridePhaseDefinitionModelDao rehydrated = sqlDao.getByRecordId(lastInserted, internalCallContext);
+                assertEquals(rehydrated.getParentPhaseName(), obj1.getParentPhaseName());
+                assertNull(rehydrated.getFixedPrice());
+                assertEquals(rehydrated.getRecurringPrice().compareTo(obj1.getRecurringPrice()), 0);
+                assertEquals(rehydrated.getCurrency(), obj1.getCurrency());
+                return null;
+            }
+        });
+    }
+
+    @Test(groups = "slow")
+    public void testGetByAttributes() throws Exception {
+
+        final CatalogOverridePhaseDefinitionModelDao objWithNoNullPrices = new CatalogOverridePhaseDefinitionModelDao("p2", "USD", BigDecimal.ZERO, new BigDecimal("12.453"), clock.getUTCNow());
+        final CatalogOverridePhaseDefinitionModelDao objWithNullFixedPrice = new CatalogOverridePhaseDefinitionModelDao("p3", "BTC", null, new BigDecimal("14.443"), clock.getUTCNow());
+        final CatalogOverridePhaseDefinitionModelDao objWithNullRecurringPrice = new CatalogOverridePhaseDefinitionModelDao("p4", "EUR", new BigDecimal("11.243"), null, clock.getUTCNow());
+
+        performTestInTransaction(new WithCatalogOverridePhaseDefinitionSqlDaoTransaction<Void>() {
+            @Override
+            public Void doTransaction(final CatalogOverridePhaseDefinitionSqlDao sqlDao) {
+                sqlDao.create(objWithNoNullPrices, internalCallContext);
+                checkRehydrated(objWithNoNullPrices, sqlDao);
+
+                sqlDao.create(objWithNullFixedPrice, internalCallContext);
+                checkRehydrated(objWithNullFixedPrice, sqlDao);
+
+                sqlDao.create(objWithNullRecurringPrice, internalCallContext);
+                checkRehydrated(objWithNullRecurringPrice, sqlDao);
+                return null;
+            }
+
+            private void checkRehydrated(final CatalogOverridePhaseDefinitionModelDao obj, final CatalogOverridePhaseDefinitionSqlDao sqlDao) {
+                final CatalogOverridePhaseDefinitionModelDao rehydrated = sqlDao.getByAttributes(obj.getParentPhaseName(), obj.getCurrency(), obj.getFixedPrice(), obj.getRecurringPrice(), internalCallContext);
+                assertEquals(rehydrated.getParentPhaseName(), obj.getParentPhaseName());
+                if (obj.getFixedPrice() != null) {
+                    assertEquals(rehydrated.getFixedPrice().compareTo(obj.getFixedPrice()), 0);
+                } else {
+                    assertNull(rehydrated.getFixedPrice());
+                }
+                if (obj.getRecurringPrice() != null) {
+                    assertEquals(rehydrated.getRecurringPrice().compareTo(obj.getRecurringPrice()), 0);
+                } else {
+                    assertNull(rehydrated.getRecurringPrice());
+                }
+                assertEquals(rehydrated.getCurrency(), obj.getCurrency());
+            }
+        });
+    }
+
+    private interface WithCatalogOverridePhaseDefinitionSqlDaoTransaction<T> {
+
+        public <T> T doTransaction(final CatalogOverridePhaseDefinitionSqlDao sqlDao);
+    }
+
+    private <T> T performTestInTransaction(final WithCatalogOverridePhaseDefinitionSqlDaoTransaction<T> callback) {
+        return dbi.inTransaction(new TransactionCallback<T>() {
+            @Override
+            public T inTransaction(final Handle handle, final TransactionStatus status) throws Exception {
+                final CatalogOverridePhaseDefinitionSqlDao sqlDao = handle.attach(CatalogOverridePhaseDefinitionSqlDao.class);
+                return callback.doTransaction(sqlDao);
+            }
+        });
+    }
+
+}
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/dao/TestCatalogOverridePlanDefinitionSqlDao.java b/catalog/src/test/java/org/killbill/billing/catalog/dao/TestCatalogOverridePlanDefinitionSqlDao.java
new file mode 100644
index 0000000..89844d8
--- /dev/null
+++ b/catalog/src/test/java/org/killbill/billing/catalog/dao/TestCatalogOverridePlanDefinitionSqlDao.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    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 org.killbill.billing.catalog.dao;
+
+import java.math.BigDecimal;
+
+import org.killbill.billing.catalog.CatalogTestSuiteWithEmbeddedDB;
+import org.killbill.commons.jdbi.mapper.LowerToCamelBeanMapperFactory;
+import org.skife.jdbi.v2.DBI;
+import org.skife.jdbi.v2.Handle;
+import org.skife.jdbi.v2.TransactionCallback;
+import org.skife.jdbi.v2.TransactionStatus;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+public class TestCatalogOverridePlanDefinitionSqlDao extends CatalogTestSuiteWithEmbeddedDB {
+
+    @BeforeClass(groups = "slow")
+    public void beforeClass() throws Exception {
+        super.beforeClass();
+        ((DBI) dbi).registerMapper(new LowerToCamelBeanMapperFactory(CatalogOverridePlanDefinitionModelDao.class));
+    }
+
+    @Test(groups = "slow")
+    public void testBasic() throws Exception {
+
+        final CatalogOverridePlanDefinitionModelDao obj1 = new CatalogOverridePlanDefinitionModelDao("p1", true, clock.getUTCNow());
+
+        performTestInTransaction(new WithCatalogOverridePlanDefinitionSqlDaoTransaction<Void>() {
+            @Override
+            public Void doTransaction(final CatalogOverridePlanDefinitionSqlDao sqlDao) {
+                sqlDao.create(obj1, internalCallContext);
+                final Long lastInserted = sqlDao.getLastInsertId();
+
+                final CatalogOverridePlanDefinitionModelDao rehydrated = sqlDao.getByRecordId(lastInserted, internalCallContext);
+                assertEquals(rehydrated.getParentPlanName(), obj1.getParentPlanName());
+                assertEquals(rehydrated.getIsActive(), obj1.getIsActive());
+                return null;
+            }
+        });
+    }
+
+    private interface WithCatalogOverridePlanDefinitionSqlDaoTransaction<T> {
+
+        public <T> T doTransaction(final CatalogOverridePlanDefinitionSqlDao sqlDao);
+    }
+
+    private <T> T performTestInTransaction(final WithCatalogOverridePlanDefinitionSqlDaoTransaction<T> callback) {
+        return dbi.inTransaction(new TransactionCallback<T>() {
+            @Override
+            public T inTransaction(final Handle handle, final TransactionStatus status) throws Exception {
+                final CatalogOverridePlanDefinitionSqlDao sqlDao = handle.attach(CatalogOverridePlanDefinitionSqlDao.class);
+                return callback.doTransaction(sqlDao);
+            }
+        });
+    }
+
+}
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/dao/TestCatalogOverridePlanPhaseSqlDao.java b/catalog/src/test/java/org/killbill/billing/catalog/dao/TestCatalogOverridePlanPhaseSqlDao.java
new file mode 100644
index 0000000..2aed21b
--- /dev/null
+++ b/catalog/src/test/java/org/killbill/billing/catalog/dao/TestCatalogOverridePlanPhaseSqlDao.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    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 org.killbill.billing.catalog.dao;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.killbill.billing.catalog.CatalogTestSuiteWithEmbeddedDB;
+import org.killbill.commons.jdbi.mapper.LowerToCamelBeanMapperFactory;
+import org.skife.jdbi.v2.DBI;
+import org.skife.jdbi.v2.Handle;
+import org.skife.jdbi.v2.TransactionCallback;
+import org.skife.jdbi.v2.TransactionStatus;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+public class TestCatalogOverridePlanPhaseSqlDao extends CatalogTestSuiteWithEmbeddedDB {
+
+
+    @BeforeClass(groups = "slow")
+    public void beforeClass() throws Exception {
+        super.beforeClass();
+        ((DBI) dbi).registerMapper(new LowerToCamelBeanMapperFactory(CatalogOverridePlanPhaseModelDao.class));
+    }
+
+    @Test(groups = "slow")
+    public void testBasic() throws Exception {
+
+        final CatalogOverridePlanPhaseModelDao obj1 = new CatalogOverridePlanPhaseModelDao((short) 1, 2L, 3L);
+
+        performTestInTransaction(new WithCatalogOverridePlanPhaseSqlDaoTransaction<Void>() {
+            @Override
+            public Void doTransaction(final CatalogOverridePlanPhaseSqlDao sqlDao) {
+                sqlDao.create(obj1, internalCallContext);
+                final Long lastInserted = sqlDao.getLastInsertId();
+
+                final CatalogOverridePlanPhaseModelDao rehydrated = sqlDao.getByRecordId(lastInserted, internalCallContext);
+                assertEquals(rehydrated.getPhaseNumber(), obj1.getPhaseNumber());
+                assertEquals(rehydrated.getPhaseDefRecordId(), obj1.getPhaseDefRecordId());
+                assertEquals(rehydrated.getTargetPlanDefRecordId(), obj1.getTargetPlanDefRecordId());
+                return null;
+            }
+        });
+    }
+
+
+    @Test(groups = "slow")
+    public void testGetTargetPlanDefinition() throws Exception {
+
+        final CatalogOverridePlanPhaseModelDao obj1 = new CatalogOverridePlanPhaseModelDao((short) 1, 2L, 3L);
+        final CatalogOverridePlanPhaseModelDao obj2 = new CatalogOverridePlanPhaseModelDao((short) 2, 5L, 3L);
+        final CatalogOverridePlanPhaseModelDao obj3 = new CatalogOverridePlanPhaseModelDao((short) 4, 7L, 3L);
+        final CatalogOverridePlanPhaseModelDao nobj1 = new CatalogOverridePlanPhaseModelDao((short) 4, 7L, 4L);
+
+        performTestInTransaction(new WithCatalogOverridePlanPhaseSqlDaoTransaction<Void>() {
+            @Override
+            public Void doTransaction(final CatalogOverridePlanPhaseSqlDao sqlDao) {
+                sqlDao.create(obj1, internalCallContext);
+                sqlDao.create(obj2, internalCallContext);
+                sqlDao.create(obj3, internalCallContext);
+                sqlDao.create(nobj1, internalCallContext);
+
+                final List<String> keys = new ArrayList<String>();
+                keys.add("1,2");
+                keys.add("2,5");
+                keys.add("4,7");
+                final Long targetPlan = sqlDao.getTargetPlanDefinition(keys, keys.size(), internalCallContext);
+                assertEquals(targetPlan, new Long(3));
+                return null;
+            }
+        });
+    }
+
+    private interface WithCatalogOverridePlanPhaseSqlDaoTransaction<T> {
+
+        public <T> T doTransaction(final CatalogOverridePlanPhaseSqlDao sqlDao);
+    }
+
+    private <T> T performTestInTransaction(final WithCatalogOverridePlanPhaseSqlDaoTransaction<T> callback) {
+        return dbi.inTransaction(new TransactionCallback<T>() {
+            @Override
+            public T inTransaction(final Handle handle, final TransactionStatus status) throws Exception {
+                final CatalogOverridePlanPhaseSqlDao sqlDao = handle.attach(CatalogOverridePlanPhaseSqlDao.class);
+                return callback.doTransaction(sqlDao);
+            }
+        });
+    }
+
+}
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/glue/TestCatalogModule.java b/catalog/src/test/java/org/killbill/billing/catalog/glue/TestCatalogModule.java
index 9af00a4..b6f5354 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/glue/TestCatalogModule.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/glue/TestCatalogModule.java
@@ -33,7 +33,6 @@ public class TestCatalogModule extends CatalogModule {
     @Override
     public void configure() {
         super.configure();
-        install(new GuicyKillbillTestNoDBModule(configSource));
         install(new MockNonEntityDaoModule(configSource));
         install(new CacheModule(configSource));
         install(new MockTenantModule(configSource));
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/glue/TestCatalogModuleNoDB.java b/catalog/src/test/java/org/killbill/billing/catalog/glue/TestCatalogModuleNoDB.java
index 16639e2..d85629c 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/glue/TestCatalogModuleNoDB.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/glue/TestCatalogModuleNoDB.java
@@ -18,11 +18,25 @@
 
 package org.killbill.billing.catalog.glue;
 
+import org.killbill.billing.GuicyKillbillTestNoDBModule;
+import org.killbill.billing.catalog.dao.CatalogOverrideDao;
 import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.mockito.Mockito;
 
 public class TestCatalogModuleNoDB extends TestCatalogModule {
 
+    protected void installCatalogDao() {
+        final CatalogOverrideDao mockCatalogOverrideDao = Mockito.mock(CatalogOverrideDao.class);
+        bind(CatalogOverrideDao.class).toInstance(mockCatalogOverrideDao);
+    }
+
     public TestCatalogModuleNoDB(final KillbillConfigSource configSource) {
         super(configSource);
     }
+
+    @Override
+    public void configure() {
+        super.configure();
+        install(new GuicyKillbillTestNoDBModule(configSource));
+    }
 }
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/glue/TestCatalogModuleWithEmbeddedDB.java b/catalog/src/test/java/org/killbill/billing/catalog/glue/TestCatalogModuleWithEmbeddedDB.java
index b1211b8..a8fa91f 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/glue/TestCatalogModuleWithEmbeddedDB.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/glue/TestCatalogModuleWithEmbeddedDB.java
@@ -1,4 +1,35 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    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 org.killbill.billing.catalog.glue;
 
-public class TestCatalogModuleWithEmbeddedDB {
+import org.killbill.billing.GuicyKillbillTestWithEmbeddedDBModule;
+import org.killbill.billing.platform.api.KillbillConfigSource;
+
+public class TestCatalogModuleWithEmbeddedDB extends TestCatalogModule {
+
+    public TestCatalogModuleWithEmbeddedDB(final KillbillConfigSource configSource) {
+        super(configSource);
+    }
+
+    @Override
+    public void configure() {
+        super.configure();
+        install(new GuicyKillbillTestWithEmbeddedDBModule(configSource));
+    }
+
 }
diff --git a/catalog/src/test/resources/resource.properties b/catalog/src/test/resources/resource.properties
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/catalog/src/test/resources/resource.properties
diff --git a/util/src/test/java/org/killbill/billing/DBTestingHelper.java b/util/src/test/java/org/killbill/billing/DBTestingHelper.java
index d61feee..d515154 100644
--- a/util/src/test/java/org/killbill/billing/DBTestingHelper.java
+++ b/util/src/test/java/org/killbill/billing/DBTestingHelper.java
@@ -157,7 +157,7 @@ public class DBTestingHelper extends PlatformDBTestingHelper {
                                "    PRIMARY KEY (record_id)\n" +
                                ");");
 
-        for (final String pack : new String[]{"account", "analytics", "beatrix", "subscription", "util", "payment", "invoice", "entitlement", "usage", "meter", "tenant"}) {
+        for (final String pack : new String[]{"catalog", "account", "analytics", "beatrix", "subscription", "util", "payment", "invoice", "entitlement", "usage", "meter", "tenant"}) {
             for (final String ddlFile : new String[]{"ddl.sql", "ddl_test.sql"}) {
                 final String ddl;
                 try {