Merge branch 'develop' of https://github.com/mifosio/portfolio into develop
diff --git a/api/src/main/java/io/mifos/individuallending/api/v1/domain/caseinstance/CaseParameters.java b/api/src/main/java/io/mifos/individuallending/api/v1/domain/caseinstance/CaseParameters.java
index a9ffe6d..9e5ad0e 100644
--- a/api/src/main/java/io/mifos/individuallending/api/v1/domain/caseinstance/CaseParameters.java
+++ b/api/src/main/java/io/mifos/individuallending/api/v1/domain/caseinstance/CaseParameters.java
@@ -24,6 +24,7 @@
 import javax.validation.Valid;
 import javax.validation.constraints.NotNull;
 import java.math.BigDecimal;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -34,8 +35,14 @@
 public final class CaseParameters {
   @ValidIdentifier
   private String customerIdentifier;
+
+  @NotNull
+  @Valid
+  private List<CreditWorthinessSnapshot> creditWorthinessSnapshots;
+
   @Range(min = 0)
   private BigDecimal maximumBalance;
+
   @NotNull
   @Valid
   private TermRange termRange;
@@ -58,6 +65,14 @@
     this.customerIdentifier = customerIdentifier;
   }
 
+  public List<CreditWorthinessSnapshot> getCreditWorthinessSnapshots() {
+    return creditWorthinessSnapshots;
+  }
+
+  public void setCreditWorthinessSnapshots(List<CreditWorthinessSnapshot> creditWorthinessSnapshots) {
+    this.creditWorthinessSnapshots = creditWorthinessSnapshots;
+  }
+
   public BigDecimal getMaximumBalance() {
     return maximumBalance;
   }
@@ -88,6 +103,7 @@
     if (o == null || getClass() != o.getClass()) return false;
     CaseParameters that = (CaseParameters) o;
     return Objects.equals(customerIdentifier, that.customerIdentifier) &&
+            Objects.equals(creditWorthinessSnapshots, that.creditWorthinessSnapshots) &&
             Objects.equals(maximumBalance, that.maximumBalance) &&
             Objects.equals(termRange, that.termRange) &&
             Objects.equals(paymentCycle, that.paymentCycle);
@@ -95,13 +111,14 @@
 
   @Override
   public int hashCode() {
-    return Objects.hash(customerIdentifier, maximumBalance, termRange, paymentCycle);
+    return Objects.hash(customerIdentifier, creditWorthinessSnapshots, maximumBalance, termRange, paymentCycle);
   }
 
   @Override
   public String toString() {
     return "CaseParameters{" +
             "customerIdentifier='" + customerIdentifier + '\'' +
+            ", creditWorthinessSnapshots=" + creditWorthinessSnapshots +
             ", maximumBalance=" + maximumBalance +
             ", termRange=" + termRange +
             ", paymentCycle=" + paymentCycle +
diff --git a/api/src/main/java/io/mifos/individuallending/api/v1/domain/caseinstance/CreditWorthinessFactor.java b/api/src/main/java/io/mifos/individuallending/api/v1/domain/caseinstance/CreditWorthinessFactor.java
new file mode 100644
index 0000000..16b2be1
--- /dev/null
+++ b/api/src/main/java/io/mifos/individuallending/api/v1/domain/caseinstance/CreditWorthinessFactor.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed 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 io.mifos.individuallending.api.v1.domain.caseinstance;
+
+import org.hibernate.validator.constraints.Length;
+import org.hibernate.validator.constraints.Range;
+
+import java.math.BigDecimal;
+import java.util.Objects;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings({"unused", "WeakerAccess"})
+public class CreditWorthinessFactor {
+  @Length(max = 4096)
+  private String description;
+
+  @Range(min = 0)
+  private BigDecimal amount;
+
+  public CreditWorthinessFactor() {
+  }
+
+  public CreditWorthinessFactor(String description, BigDecimal amount) {
+    this.description = description;
+    this.amount = amount;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+
+  public void setDescription(String description) {
+    this.description = description;
+  }
+
+  public BigDecimal getAmount() {
+    return amount;
+  }
+
+  public void setAmount(BigDecimal amount) {
+    this.amount = amount;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    CreditWorthinessFactor that = (CreditWorthinessFactor) o;
+    return Objects.equals(description, that.description) &&
+            Objects.equals(amount, that.amount);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(description, amount);
+  }
+
+  @Override
+  public String toString() {
+    return "CreditWorthinessFactor{" +
+            "description='" + description + '\'' +
+            ", amount=" + amount +
+            '}';
+  }
+}
\ No newline at end of file
diff --git a/api/src/main/java/io/mifos/individuallending/api/v1/domain/caseinstance/CreditWorthinessSnapshot.java b/api/src/main/java/io/mifos/individuallending/api/v1/domain/caseinstance/CreditWorthinessSnapshot.java
new file mode 100644
index 0000000..65db8f1
--- /dev/null
+++ b/api/src/main/java/io/mifos/individuallending/api/v1/domain/caseinstance/CreditWorthinessSnapshot.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed 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 io.mifos.individuallending.api.v1.domain.caseinstance;
+
+import io.mifos.core.lang.validation.constraints.ValidIdentifier;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings({"WeakerAccess", "unused"})
+public class CreditWorthinessSnapshot {
+  @ValidIdentifier
+  private String forCustomer;
+
+  @NotNull
+  @Valid
+  private List<CreditWorthinessFactor> incomeSources;
+
+  @NotNull
+  @Valid
+  private List<CreditWorthinessFactor> assets;
+
+  @NotNull
+  @Valid
+  private List<CreditWorthinessFactor> debts;
+
+  public CreditWorthinessSnapshot() {
+  }
+
+  public CreditWorthinessSnapshot(String forCustomer) {
+    this.forCustomer = forCustomer;
+  }
+
+  public String getForCustomer() {
+    return forCustomer;
+  }
+
+  public void setForCustomer(String forCustomer) {
+    this.forCustomer = forCustomer;
+  }
+
+  public List<CreditWorthinessFactor> getIncomeSources() {
+    return incomeSources;
+  }
+
+  public void setIncomeSources(List<CreditWorthinessFactor> incomeSources) {
+    this.incomeSources = incomeSources;
+  }
+
+  public List<CreditWorthinessFactor> getAssets() {
+    return assets;
+  }
+
+  public void setAssets(List<CreditWorthinessFactor> assets) {
+    this.assets = assets;
+  }
+
+  public List<CreditWorthinessFactor> getDebts() {
+    return debts;
+  }
+
+  public void setDebts(List<CreditWorthinessFactor> debts) {
+    this.debts = debts;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    CreditWorthinessSnapshot that = (CreditWorthinessSnapshot) o;
+    return Objects.equals(forCustomer, that.forCustomer) &&
+            Objects.equals(incomeSources, that.incomeSources) &&
+            Objects.equals(assets, that.assets) &&
+            Objects.equals(debts, that.debts);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(forCustomer, incomeSources, assets, debts);
+  }
+
+  @Override
+  public String toString() {
+    return "CreditWorthinessSnapshot{" +
+            "forCustomer='" + forCustomer + '\'' +
+            ", incomeSources=" + incomeSources +
+            ", assets=" + assets +
+            ", debts=" + debts +
+            '}';
+  }
+}
diff --git a/api/src/test/java/io/mifos/Fixture.java b/api/src/test/java/io/mifos/Fixture.java
index ac4f35c..dfc89fe 100644
--- a/api/src/test/java/io/mifos/Fixture.java
+++ b/api/src/test/java/io/mifos/Fixture.java
@@ -17,11 +17,14 @@
 
 import com.google.gson.Gson;
 import io.mifos.individuallending.api.v1.domain.caseinstance.CaseParameters;
+import io.mifos.individuallending.api.v1.domain.caseinstance.CreditWorthinessFactor;
+import io.mifos.individuallending.api.v1.domain.caseinstance.CreditWorthinessSnapshot;
 import io.mifos.individuallending.api.v1.domain.product.ProductParameters;
 import io.mifos.portfolio.api.v1.domain.*;
 
 import java.math.BigDecimal;
 import java.time.temporal.ChronoUnit;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
@@ -124,6 +127,18 @@
     ret.setTermRange(new TermRange(ChronoUnit.MONTHS, 18));
     ret.setPaymentCycle(new PaymentCycle(ChronoUnit.MONTHS, 1, 1, null, null));
 
+    final CreditWorthinessSnapshot customerCreditWorthinessSnapshot = new CreditWorthinessSnapshot();
+    customerCreditWorthinessSnapshot.setForCustomer("alice");
+    customerCreditWorthinessSnapshot.setDebts(Collections.singletonList(new CreditWorthinessFactor("some debt", fixScale(BigDecimal.valueOf(300)))));
+    customerCreditWorthinessSnapshot.setAssets(Collections.singletonList(new CreditWorthinessFactor("some asset", fixScale(BigDecimal.valueOf(500)))));
+    customerCreditWorthinessSnapshot.setIncomeSources(Collections.singletonList(new CreditWorthinessFactor("some income source", fixScale(BigDecimal.valueOf(300)))));
+    final CreditWorthinessSnapshot cosignerCreditWorthinessSnapshot = new CreditWorthinessSnapshot();
+    cosignerCreditWorthinessSnapshot.setForCustomer("seema");
+    cosignerCreditWorthinessSnapshot.setDebts(Collections.emptyList());
+    cosignerCreditWorthinessSnapshot.setAssets(Collections.singletonList(new CreditWorthinessFactor("a house", fixScale(BigDecimal.valueOf(50000)))));
+    cosignerCreditWorthinessSnapshot.setIncomeSources(Collections.singletonList(new CreditWorthinessFactor("retirement", fixScale(BigDecimal.valueOf(200)))));
+    ret.setCreditWorthinessSnapshots(Arrays.asList(customerCreditWorthinessSnapshot, cosignerCreditWorthinessSnapshot));
+
     return ret;
   }
 
diff --git a/api/src/test/java/io/mifos/individuallending/api/v1/domain/caseinstance/CaseParametersTest.java b/api/src/test/java/io/mifos/individuallending/api/v1/domain/caseinstance/CaseParametersTest.java
index 480cf56..decfa89 100644
--- a/api/src/test/java/io/mifos/individuallending/api/v1/domain/caseinstance/CaseParametersTest.java
+++ b/api/src/test/java/io/mifos/individuallending/api/v1/domain/caseinstance/CaseParametersTest.java
@@ -18,10 +18,7 @@
 import io.mifos.Fixture;
 import io.mifos.core.test.domain.ValidationTest;
 import io.mifos.core.test.domain.ValidationTestCase;
-import io.mifos.portfolio.api.v1.domain.AccountAssignment;
 import io.mifos.portfolio.api.v1.domain.TermRange;
-import org.junit.Assert;
-import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
@@ -51,6 +48,7 @@
   public static Collection testCases() {
     final Collection<ValidationTestCase> ret = new ArrayList<>();
 
+    ret.add(new ValidationTestCase<CaseParameters>("valid"));
     ret.add(new ValidationTestCase<CaseParameters>("nullTermRange")
             .adjustment(x -> x.setTermRange(null))
             .valid(false));
@@ -66,6 +64,9 @@
     ret.add(new ValidationTestCase<CaseParameters>("invalid payment cycle unit")
             .adjustment(x -> x.getPaymentCycle().setTemporalUnit(ChronoUnit.SECONDS))
             .valid(false));
+    ret.add(new ValidationTestCase<CaseParameters>("null CreditWorthinessFactor list")
+            .adjustment(x -> x.setCreditWorthinessSnapshots(null))
+            .valid(false));
 
     return ret;
   }
diff --git a/api/src/test/java/io/mifos/individuallending/api/v1/domain/caseinstance/CreditWorthinessFactorTest.java b/api/src/test/java/io/mifos/individuallending/api/v1/domain/caseinstance/CreditWorthinessFactorTest.java
new file mode 100644
index 0000000..134263b
--- /dev/null
+++ b/api/src/test/java/io/mifos/individuallending/api/v1/domain/caseinstance/CreditWorthinessFactorTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed 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 io.mifos.individuallending.api.v1.domain.caseinstance;
+
+import io.mifos.core.test.domain.ValidationTest;
+import io.mifos.core.test.domain.ValidationTestCase;
+import org.apache.commons.lang.RandomStringUtils;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * @author Myrle Krantz
+ */
+@RunWith(Parameterized.class)
+public class CreditWorthinessFactorTest extends ValidationTest<CreditWorthinessFactor> {
+  public CreditWorthinessFactorTest(ValidationTestCase<CreditWorthinessFactor> testCase) {
+    super(testCase);
+  }
+
+  @Override
+  protected CreditWorthinessFactor createValidTestSubject() {
+    return getValidTestSubject();
+  }
+
+  static CreditWorthinessFactor getValidTestSubject() {
+    final CreditWorthinessFactor ret = new CreditWorthinessFactor();
+    ret.setAmount(BigDecimal.ONE);
+    ret.setDescription(null);
+    return ret;
+  }
+
+  @Parameterized.Parameters
+  public static Collection testCases() {
+    final Collection<ValidationTestCase> ret = new ArrayList<>();
+    ret.add(new ValidationTestCase<CreditWorthinessFactor>("valid"));
+    ret.add(new ValidationTestCase<CreditWorthinessFactor>("nullDescription")
+            .adjustment(x -> x.setDescription(null))
+            .valid(true));
+    ret.add(new ValidationTestCase<CreditWorthinessFactor>("negative amount")
+            .adjustment(x -> x.setAmount(BigDecimal.valueOf(-1)))
+            .valid(false));
+    ret.add(new ValidationTestCase<CreditWorthinessFactor>("very long description")
+            .adjustment(x -> x.setDescription(RandomStringUtils.randomAlphanumeric(4096)))
+            .valid(true));
+    ret.add(new ValidationTestCase<CreditWorthinessFactor>("too long description")
+            .adjustment(x -> x.setDescription(RandomStringUtils.randomAlphanumeric(4097)))
+            .valid(false));
+
+
+    return ret;
+  }
+}
diff --git a/api/src/test/java/io/mifos/individuallending/api/v1/domain/caseinstance/CreditWorthinessSnapshotTest.java b/api/src/test/java/io/mifos/individuallending/api/v1/domain/caseinstance/CreditWorthinessSnapshotTest.java
new file mode 100644
index 0000000..6bfa4df
--- /dev/null
+++ b/api/src/test/java/io/mifos/individuallending/api/v1/domain/caseinstance/CreditWorthinessSnapshotTest.java
@@ -0,0 +1,62 @@
+package io.mifos.individuallending.api.v1.domain.caseinstance;
+
+import io.mifos.core.test.domain.ValidationTest;
+import io.mifos.core.test.domain.ValidationTestCase;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+
+/**
+ * @author Myrle Krantz
+ */
+@RunWith(Parameterized.class)
+public class CreditWorthinessSnapshotTest extends ValidationTest<CreditWorthinessSnapshot> {
+  public CreditWorthinessSnapshotTest(ValidationTestCase<CreditWorthinessSnapshot> testCase) {
+    super(testCase);
+  }
+
+  @Override
+  protected CreditWorthinessSnapshot createValidTestSubject() {
+    final CreditWorthinessSnapshot ret = new CreditWorthinessSnapshot();
+    ret.setForCustomer("gracie");
+    ret.setDebts(Collections.emptyList());
+    ret.setAssets(Collections.emptyList());
+    ret.setIncomeSources(Collections.emptyList());
+    return ret;
+  }
+
+  @Parameterized.Parameters
+  public static Collection testCases() {
+    final Collection<ValidationTestCase> ret = new ArrayList<>();
+    ret.add(new ValidationTestCase<CreditWorthinessSnapshot>("valid"));
+    ret.add(new ValidationTestCase<CreditWorthinessSnapshot>("nullCustomer")
+            .adjustment(x -> x.setForCustomer(null))
+            .valid(false));
+    ret.add(new ValidationTestCase<CreditWorthinessSnapshot>("null debts")
+            .adjustment(x -> x.setDebts(null))
+            .valid(false));
+    ret.add(new ValidationTestCase<CreditWorthinessSnapshot>("null assets")
+            .adjustment(x -> x.setAssets(null))
+            .valid(false));
+    ret.add(new ValidationTestCase<CreditWorthinessSnapshot>("null income sources")
+            .adjustment(x -> x.setIncomeSources(null))
+            .valid(false));
+    ret.add(new ValidationTestCase<CreditWorthinessSnapshot>("one valid income sources")
+            .adjustment(x -> x.setIncomeSources(Collections.singletonList(CreditWorthinessFactorTest.getValidTestSubject())))
+            .valid(true));
+    ret.add(new ValidationTestCase<CreditWorthinessSnapshot>("one invalid income sources")
+            .adjustment(x -> {
+              final CreditWorthinessFactor elt = CreditWorthinessFactorTest.getValidTestSubject();
+              elt.setAmount(BigDecimal.valueOf(-1));
+              x.setIncomeSources(Collections.singletonList(elt));
+            })
+            .valid(false));
+
+    return ret;
+  }
+}
diff --git a/api/src/test/java/io/mifos/portfolio/api/v1/domain/CommandTest.java b/api/src/test/java/io/mifos/portfolio/api/v1/domain/CommandTest.java
index d239a4c..20cf510 100644
--- a/api/src/test/java/io/mifos/portfolio/api/v1/domain/CommandTest.java
+++ b/api/src/test/java/io/mifos/portfolio/api/v1/domain/CommandTest.java
@@ -34,16 +34,13 @@
 
   @Override
   protected Command createValidTestSubject() {
-    return new Command("blah", null);
+    return new Command();
   }
 
   @Parameterized.Parameters
   public static Collection testCases() {
     final Collection<ValidationTestCase> ret = new ArrayList<>();
     ret.add(new ValidationTestCase<Command>("valid"));
-    ret.add(new ValidationTestCase<Command>("nullAction")
-            .adjustment(x -> x.setAction(null))
-            .valid(false));
     return ret;
   }
 }
diff --git a/component-test/src/main/java/io/mifos/portfolio/Fixture.java b/component-test/src/main/java/io/mifos/portfolio/Fixture.java
index d11aa34..e6b7bbf 100644
--- a/component-test/src/main/java/io/mifos/portfolio/Fixture.java
+++ b/component-test/src/main/java/io/mifos/portfolio/Fixture.java
@@ -16,12 +16,15 @@
 package io.mifos.portfolio;
 
 import com.google.gson.Gson;
+import io.mifos.individuallending.api.v1.domain.caseinstance.CreditWorthinessFactor;
+import io.mifos.individuallending.api.v1.domain.caseinstance.CreditWorthinessSnapshot;
 import io.mifos.portfolio.api.v1.domain.*;
 import io.mifos.individuallending.api.v1.domain.caseinstance.CaseParameters;
 import io.mifos.individuallending.api.v1.domain.product.ProductParameters;
 
 import java.math.BigDecimal;
 import java.time.temporal.ChronoUnit;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
@@ -128,6 +131,20 @@
     ret.setTermRange(new TermRange(ChronoUnit.MONTHS, 18));
     ret.setPaymentCycle(new PaymentCycle(ChronoUnit.MONTHS, 1, 1, null, null));
 
+    final CreditWorthinessSnapshot customerCreditWorthinessSnapshot = new CreditWorthinessSnapshot();
+    customerCreditWorthinessSnapshot.setForCustomer("alice");
+    customerCreditWorthinessSnapshot.setDebts(Collections.singletonList(new CreditWorthinessFactor("some debt", fixScale(BigDecimal.valueOf(300)))));
+    customerCreditWorthinessSnapshot.setAssets(Collections.singletonList(new CreditWorthinessFactor("some asset", fixScale(BigDecimal.valueOf(500)))));
+    customerCreditWorthinessSnapshot.setIncomeSources(Collections.singletonList(new CreditWorthinessFactor("some income source", fixScale(BigDecimal.valueOf(300)))));
+
+    final CreditWorthinessSnapshot cosignerCreditWorthinessSnapshot = new CreditWorthinessSnapshot();
+    cosignerCreditWorthinessSnapshot.setForCustomer("seema");
+    cosignerCreditWorthinessSnapshot.setDebts(Collections.emptyList());
+    cosignerCreditWorthinessSnapshot.setAssets(Collections.singletonList(new CreditWorthinessFactor("a house", fixScale(BigDecimal.valueOf(50000)))));
+    cosignerCreditWorthinessSnapshot.setIncomeSources(Collections.singletonList(new CreditWorthinessFactor("retirement", fixScale(BigDecimal.valueOf(200)))));
+
+    ret.setCreditWorthinessSnapshots(Arrays.asList(customerCreditWorthinessSnapshot, cosignerCreditWorthinessSnapshot));
+
     return ret;
   }
 
diff --git a/component-test/src/main/java/io/mifos/portfolio/TestCases.java b/component-test/src/main/java/io/mifos/portfolio/TestCases.java
index e897070..37e4619 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestCases.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestCases.java
@@ -18,6 +18,7 @@
 import com.google.gson.Gson;
 import io.mifos.core.test.domain.TimeStampChecker;
 import io.mifos.individuallending.api.v1.domain.caseinstance.CaseParameters;
+import io.mifos.individuallending.api.v1.domain.caseinstance.CreditWorthinessFactor;
 import io.mifos.portfolio.api.v1.domain.AccountAssignment;
 import io.mifos.portfolio.api.v1.domain.Case;
 import io.mifos.portfolio.api.v1.domain.CasePage;
@@ -28,9 +29,7 @@
 import org.junit.Test;
 
 import java.math.BigDecimal;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -78,6 +77,9 @@
     newCaseParameters.setMaximumBalance(Fixture.fixScale(BigDecimal.TEN));
     newCaseParameters.getPaymentCycle().setAlignmentDay(1);
     newCaseParameters.getPaymentCycle().setAlignmentWeek(2);
+
+    newCaseParameters.setCreditWorthinessSnapshots(Collections.emptyList());
+
     final String changedParameters = new Gson().toJson(newCaseParameters);
     caseInstance.setParameters(changedParameters);
 
@@ -98,6 +100,52 @@
   }
 
   @Test
+  public void shouldRemoveCosigner() throws InterruptedException {
+    final Product product = createAndEnableProduct();
+
+    final CaseParameters caseParameters = Fixture.createAdjustedCaseParameters(x -> {});
+    final String caseParametersAsString = new Gson().toJson(caseParameters);
+    final Case caseInstance = createAdjustedCase(product.getIdentifier(), x -> x.setParameters(caseParametersAsString));
+
+    caseParameters.setCreditWorthinessSnapshots(Collections.singletonList(caseParameters.getCreditWorthinessSnapshots().get(0)));
+    final String changedParameters = new Gson().toJson(caseParameters);
+    caseInstance.setParameters(changedParameters);
+
+    portfolioManager.changeCase(product.getIdentifier(), caseInstance.getIdentifier(), caseInstance);
+    Assert.assertTrue(this.eventRecorder.wait(EventConstants.PUT_CASE,
+            new CaseEvent(product.getIdentifier(), caseInstance.getIdentifier())));
+
+    final Case caseAsSaved = portfolioManager.getCase(product.getIdentifier(), caseInstance.getIdentifier());
+
+    Assert.assertEquals(caseInstance, caseAsSaved);
+  }
+
+  @Test
+  public void shouldAddDebt() throws InterruptedException {
+    final Product product = createAndEnableProduct();
+
+    final CaseParameters caseParameters = Fixture.createAdjustedCaseParameters(x -> {});
+    final String caseParametersAsString = new Gson().toJson(caseParameters);
+    final Case caseInstance = createAdjustedCase(product.getIdentifier(), x -> x.setParameters(caseParametersAsString));
+
+    final List<CreditWorthinessFactor> debts = caseParameters.getCreditWorthinessSnapshots().get(0).getDebts();
+    final ArrayList<CreditWorthinessFactor> newDebts = new ArrayList<>();
+    newDebts.addAll(debts);
+    newDebts.add(new CreditWorthinessFactor("boop", BigDecimal.valueOf(5, 4)));
+    caseParameters.getCreditWorthinessSnapshots().get(0).setDebts(newDebts);
+    final String changedParameters = new Gson().toJson(caseParameters);
+    caseInstance.setParameters(changedParameters);
+
+    portfolioManager.changeCase(product.getIdentifier(), caseInstance.getIdentifier(), caseInstance);
+    Assert.assertTrue(this.eventRecorder.wait(EventConstants.PUT_CASE,
+            new CaseEvent(product.getIdentifier(), caseInstance.getIdentifier())));
+
+    final Case caseAsSaved = portfolioManager.getCase(product.getIdentifier(), caseInstance.getIdentifier());
+
+    Assert.assertEquals(caseInstance, caseAsSaved);
+  }
+
+  @Test
   public void shouldRemoveCaseAccountAssignments() throws InterruptedException {
     final Product product = createAndEnableProduct();
 
diff --git a/component-test/src/main/java/io/mifos/portfolio/TestProducts.java b/component-test/src/main/java/io/mifos/portfolio/TestProducts.java
index 2028e57..4b73bf3 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestProducts.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestProducts.java
@@ -164,6 +164,7 @@
   public void shouldChangeProductAccountAssignments() throws InterruptedException {
     final Product product = createAdjustedProduct(x -> x.setAccountAssignments(Collections.emptySet()));
 
+    //Add account assignments
     final Set<AccountAssignment> incompleteAccountAssignments = portfolioManager.getIncompleteAccountAssignments(product.getIdentifier());
 
     final Set<AccountAssignment> accountAssignments = incompleteAccountAssignments.stream()
@@ -178,11 +179,24 @@
     portfolioManager.changeProduct(product.getIdentifier(), product);
     Assert.assertTrue(this.eventRecorder.wait(EventConstants.PUT_PRODUCT, product.getIdentifier()));
 
-    final Product productAsSaved = portfolioManager.getProduct(product.getIdentifier());
+    {
+      final Product productAsSaved = portfolioManager.getProduct(product.getIdentifier());
 
-    Assert.assertEquals(product, productAsSaved);
-    Assert.assertEquals(TEST_USER, productAsSaved.getLastModifiedBy());
-    timeStampChecker.assertCorrect(productAsSaved.getLastModifiedOn());
+      Assert.assertEquals(product, productAsSaved);
+      Assert.assertEquals(TEST_USER, productAsSaved.getLastModifiedBy());
+      timeStampChecker.assertCorrect(productAsSaved.getLastModifiedOn());
+    }
+
+    //Remove account assignments
+    product.setAccountAssignments(Collections.emptySet());
+    portfolioManager.changeProduct(product.getIdentifier(), product);
+    Assert.assertTrue(this.eventRecorder.wait(EventConstants.PUT_PRODUCT, product.getIdentifier()));
+
+    {
+      final Product productAsSaved = portfolioManager.getProduct(product.getIdentifier());
+
+      Assert.assertEquals(product, productAsSaved);
+    }
   }
 
   @Test
diff --git a/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java b/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
index 1359c67..c296ad2 100644
--- a/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
+++ b/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
@@ -16,17 +16,19 @@
 package io.mifos.individuallending;
 
 import com.google.gson.Gson;
-import io.mifos.individuallending.internal.mapper.CaseParametersMapper;
-import io.mifos.individuallending.internal.repository.CaseParametersEntity;
-import io.mifos.individuallending.internal.repository.CaseParametersRepository;
-import io.mifos.products.spi.ProductCommandDispatcher;
-import io.mifos.products.spi.PatternFactory;
-import io.mifos.portfolio.service.ServiceConstants;
 import io.mifos.individuallending.api.v1.domain.caseinstance.CaseParameters;
 import io.mifos.individuallending.api.v1.domain.workflow.Action;
+import io.mifos.individuallending.internal.mapper.CaseParametersMapper;
+import io.mifos.individuallending.internal.repository.CaseCreditWorthinessFactorEntity;
+import io.mifos.individuallending.internal.repository.CaseParametersEntity;
+import io.mifos.individuallending.internal.repository.CaseParametersRepository;
+import io.mifos.individuallending.internal.repository.CreditWorthinessFactorType;
 import io.mifos.portfolio.api.v1.domain.Case;
 import io.mifos.portfolio.api.v1.domain.ChargeDefinition;
 import io.mifos.portfolio.api.v1.domain.Pattern;
+import io.mifos.portfolio.service.ServiceConstants;
+import io.mifos.products.spi.PatternFactory;
+import io.mifos.products.spi.ProductCommandDispatcher;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Component;
@@ -170,6 +172,33 @@
     caseParametersRepository.save(caseParametersEntity);
   }
 
+  private static class CaseCreditWorthinessFactorUniquenessCriteria {
+    String customerId;
+    CreditWorthinessFactorType factorType;
+    int position;
+
+    CaseCreditWorthinessFactorUniquenessCriteria(final CaseCreditWorthinessFactorEntity entity) {
+      this.customerId = entity.getCustomerIdentifier();
+      this.factorType = entity.getFactorType();
+      this.position = entity.getPositionInFactor();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+      CaseCreditWorthinessFactorUniquenessCriteria that = (CaseCreditWorthinessFactorUniquenessCriteria) o;
+      return position == that.position &&
+              Objects.equals(customerId, that.customerId) &&
+              factorType == that.factorType;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(customerId, factorType, position);
+    }
+  }
+
   @Transactional
   @Override
   public void changeParameters(Long caseId, String parameters) {
@@ -188,6 +217,20 @@
     oldCaseParameters.setPaymentCycleAlignmentWeek(caseParameters.getPaymentCycle().getAlignmentWeek());
     oldCaseParameters.setPaymentCycleAlignmentMonth(caseParameters.getPaymentCycle().getAlignmentMonth());
 
+
+
+    final Set<CaseCreditWorthinessFactorEntity> oldCreditWorthinessFactorEntities = oldCaseParameters.getCreditWorthinessFactors();
+    final Map<CaseCreditWorthinessFactorUniquenessCriteria, CaseCreditWorthinessFactorEntity> forFindingThings = oldCreditWorthinessFactorEntities.stream()
+            .collect(Collectors.toMap(CaseCreditWorthinessFactorUniquenessCriteria::new, x -> x));
+
+    final Set<CaseCreditWorthinessFactorEntity> newCreditWorthinessFactorEntities = CaseParametersMapper.mapSnapshotsToFactors(caseParameters.getCreditWorthinessSnapshots(),oldCaseParameters);
+    newCreditWorthinessFactorEntities.forEach(x -> {
+      final CaseCreditWorthinessFactorEntity existingThing = forFindingThings.get(new CaseCreditWorthinessFactorUniquenessCriteria(x));
+      if (existingThing != null) x.setId(existingThing.getId());
+    });
+    oldCaseParameters.getCreditWorthinessFactors().clear();
+    oldCaseParameters.getCreditWorthinessFactors().addAll(newCreditWorthinessFactorEntities);
+
     caseParametersRepository.save(oldCaseParameters);
   }
 
diff --git a/service/src/main/java/io/mifos/individuallending/internal/mapper/CaseParametersMapper.java b/service/src/main/java/io/mifos/individuallending/internal/mapper/CaseParametersMapper.java
index a0f4983..6de6fc9 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/mapper/CaseParametersMapper.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/mapper/CaseParametersMapper.java
@@ -16,10 +16,21 @@
 package io.mifos.individuallending.internal.mapper;
 
 import io.mifos.individuallending.api.v1.domain.caseinstance.CaseParameters;
+import io.mifos.individuallending.api.v1.domain.caseinstance.CreditWorthinessFactor;
+import io.mifos.individuallending.api.v1.domain.caseinstance.CreditWorthinessSnapshot;
+import io.mifos.individuallending.internal.repository.CaseCreditWorthinessFactorEntity;
 import io.mifos.individuallending.internal.repository.CaseParametersEntity;
+import io.mifos.individuallending.internal.repository.CreditWorthinessFactorType;
 import io.mifos.portfolio.api.v1.domain.PaymentCycle;
 import io.mifos.portfolio.api.v1.domain.TermRange;
 
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
 /**
  * @author Myrle Krantz
  */
@@ -39,10 +50,50 @@
     ret.setPaymentCycleAlignmentDay(instance.getPaymentCycle().getAlignmentDay());
     ret.setPaymentCycleAlignmentWeek(instance.getPaymentCycle().getAlignmentWeek());
     ret.setPaymentCycleAlignmentMonth(instance.getPaymentCycle().getAlignmentMonth());
+    ret.setCreditWorthinessFactors(mapSnapshotsToFactors(instance.getCreditWorthinessSnapshots(), ret));
 
     return ret;
   }
 
+  public static Set<CaseCreditWorthinessFactorEntity> mapSnapshotsToFactors(
+          final List<CreditWorthinessSnapshot> creditWorthinessSnapshots,
+          final CaseParametersEntity caseParametersEntity) {
+    return Stream.iterate(0, i -> i+1).limit(creditWorthinessSnapshots.size())
+            .flatMap(i -> {
+      final String customerId = creditWorthinessSnapshots.get(i).getForCustomer();
+      return Stream.concat(Stream.concat(
+              mapSnapshotPartToFactors(customerId, i,
+                      CreditWorthinessFactorType.ASSET, creditWorthinessSnapshots.get(i).getAssets(),
+                      caseParametersEntity),
+              mapSnapshotPartToFactors(customerId, i,
+                      CreditWorthinessFactorType.INCOME_SOURCE, creditWorthinessSnapshots.get(i).getIncomeSources(),
+                      caseParametersEntity)),
+              mapSnapshotPartToFactors(customerId, i,
+                      CreditWorthinessFactorType.DEBT, creditWorthinessSnapshots.get(i).getDebts(),
+                      caseParametersEntity));
+    }).collect(Collectors.toSet());
+  }
+
+  private static Stream<CaseCreditWorthinessFactorEntity> mapSnapshotPartToFactors(
+          final String forCustomer,
+          final int i,
+          final CreditWorthinessFactorType factorType,
+          final List<CreditWorthinessFactor> part,
+          final CaseParametersEntity caseParametersEntity) {
+    return Stream.iterate(0, j -> j+1).limit(part.size())
+            .map(j -> {
+              final CaseCreditWorthinessFactorEntity ret = new CaseCreditWorthinessFactorEntity();
+              ret.setAmount(part.get(j).getAmount());
+              ret.setDescription(part.get(j).getDescription());
+              ret.setCaseId(caseParametersEntity);
+              ret.setCustomerIdentifier(forCustomer);
+              ret.setFactorType(factorType);
+              ret.setPositionInFactor(j);
+              ret.setPositionInCustomers(i);
+              return ret;
+            });
+  }
+
   private static TermRange getTermRange(final CaseParametersEntity instance) {
     final TermRange ret = new TermRange();
 
@@ -67,9 +118,64 @@
   public static CaseParameters mapEntity(final CaseParametersEntity caseParametersEntity) {
     final CaseParameters ret = new CaseParameters();
     ret.setCustomerIdentifier(caseParametersEntity.getCustomerIdentifier());
+    ret.setCreditWorthinessSnapshots(mapFactorsToSnapshots(caseParametersEntity.getCreditWorthinessFactors()));
     ret.setTermRange(getTermRange(caseParametersEntity));
     ret.setMaximumBalance(caseParametersEntity.getBalanceRangeMaximum());
     ret.setPaymentCycle(getPaymentCycle(caseParametersEntity));
     return ret;
   }
+
+  private static List<CreditWorthinessSnapshot> mapFactorsToSnapshots(
+          final Set<CaseCreditWorthinessFactorEntity> creditWorthinessFactors) {
+    final Map<Integer, Set<CaseCreditWorthinessFactorEntity>> groupedByCustomerId
+            = creditWorthinessFactors.stream()
+            .collect(Collectors.groupingBy(CaseCreditWorthinessFactorEntity::getPositionInCustomers, Collectors.toSet()));
+
+    return groupedByCustomerId.entrySet().stream()
+            .sorted((x, y) -> Integer.compare(x.getKey(), y.getKey()))
+            .map(CaseParametersMapper::mapEntryToSnapshot).collect(Collectors.toList());
+  }
+
+  private static CreditWorthinessSnapshot mapEntryToSnapshot(
+          final Map.Entry<Integer, Set<CaseCreditWorthinessFactorEntity>> customerEntry) {
+    final CreditWorthinessSnapshot ret = new CreditWorthinessSnapshot();
+
+    final Map<CreditWorthinessFactorType, Set<CaseCreditWorthinessFactorEntity>> groupedByFactorType
+            = customerEntry.getValue().stream()
+            .collect(Collectors.groupingBy(CaseCreditWorthinessFactorEntity::getFactorType, Collectors.toSet()));
+    ret.setAssets(getFactorsByType(groupedByFactorType, CreditWorthinessFactorType.ASSET));
+    ret.setDebts(getFactorsByType(groupedByFactorType, CreditWorthinessFactorType.DEBT));
+    ret.setIncomeSources(getFactorsByType(groupedByFactorType, CreditWorthinessFactorType.INCOME_SOURCE));
+
+    final String customerId = customerEntry.getValue().stream()
+            .findFirst()
+            .map(CaseCreditWorthinessFactorEntity::getCustomerIdentifier)
+            .orElse("");
+    ret.setForCustomer(customerId);
+
+    return ret;
+  }
+
+  private static List<CreditWorthinessFactor> getFactorsByType(
+          final Map<CreditWorthinessFactorType, Set<CaseCreditWorthinessFactorEntity>> groupedByFactorType,
+          final CreditWorthinessFactorType factorType) {
+    final Set<CaseCreditWorthinessFactorEntity> byFactorType = groupedByFactorType.get(factorType);
+    if (byFactorType == null)
+      return Collections.emptyList();
+    else {
+      return byFactorType.stream()
+              .sorted((x, y) -> Integer.compare(x.getPositionInFactor(), y.getPositionInFactor()))
+              .map(CaseParametersMapper::mapEntryToFactor)
+              .collect(Collectors.toList());
+
+    }
+  }
+
+  private static CreditWorthinessFactor mapEntryToFactor(
+          final CaseCreditWorthinessFactorEntity caseCreditWorthinessFactorEntity) {
+    final CreditWorthinessFactor ret = new CreditWorthinessFactor();
+    ret.setDescription(caseCreditWorthinessFactorEntity.getDescription());
+    ret.setAmount(caseCreditWorthinessFactorEntity.getAmount());
+    return ret;
+  }
 }
diff --git a/service/src/main/java/io/mifos/individuallending/internal/repository/CaseCreditWorthinessFactorEntity.java b/service/src/main/java/io/mifos/individuallending/internal/repository/CaseCreditWorthinessFactorEntity.java
new file mode 100644
index 0000000..3906475
--- /dev/null
+++ b/service/src/main/java/io/mifos/individuallending/internal/repository/CaseCreditWorthinessFactorEntity.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed 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 io.mifos.individuallending.internal.repository;
+
+import javax.persistence.*;
+import java.math.BigDecimal;
+import java.util.Objects;
+
+/**
+ * @author Myrle Krantz
+ */
+@Entity
+@Table(name = "bastet_il_c_credit_facts")
+public class CaseCreditWorthinessFactorEntity {
+  @Id
+  @GeneratedValue(strategy = GenerationType.IDENTITY)
+  @Column(name = "id")
+  private Long id;
+
+  @ManyToOne(fetch = FetchType.LAZY)
+  @JoinColumn(name = "case_id", nullable = false)
+  private CaseParametersEntity caseId;
+
+  @Column(name = "customer_identifier")
+  private String customerIdentifier;
+
+  @Column(name = "position_in_factor")
+  private Integer positionInFactor;
+
+  @Column(name = "position_in_customers")
+  private Integer positionInCustomers;
+
+  @Enumerated(EnumType.STRING)
+  @Column(name = "fact_type")
+  private CreditWorthinessFactorType factorType;
+
+  @Column(name = "description")
+  private String description;
+
+  @Column(name = "amount")
+  private BigDecimal amount;
+
+  public Long getId() {
+    return id;
+  }
+
+  public void setId(Long id) {
+    this.id = id;
+  }
+
+  public CaseParametersEntity getCaseId() {
+    return caseId;
+  }
+
+  public void setCaseId(CaseParametersEntity caseId) {
+    this.caseId = caseId;
+  }
+
+  public String getCustomerIdentifier() {
+    return customerIdentifier;
+  }
+
+  public void setCustomerIdentifier(String customerIdentifier) {
+    this.customerIdentifier = customerIdentifier;
+  }
+
+  public Integer getPositionInFactor() {
+    return positionInFactor;
+  }
+
+  public void setPositionInFactor(Integer positionInFactor) {
+    this.positionInFactor = positionInFactor;
+  }
+
+  public Integer getPositionInCustomers() {
+    return positionInCustomers;
+  }
+
+  public void setPositionInCustomers(Integer positionInCustomers) {
+    this.positionInCustomers = positionInCustomers;
+  }
+
+  public CreditWorthinessFactorType getFactorType() {
+    return factorType;
+  }
+
+  public void setFactorType(CreditWorthinessFactorType factorType) {
+    this.factorType = factorType;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+
+  public void setDescription(String description) {
+    this.description = description;
+  }
+
+  public BigDecimal getAmount() {
+    return amount;
+  }
+
+  public void setAmount(BigDecimal amount) {
+    this.amount = amount;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    CaseCreditWorthinessFactorEntity that = (CaseCreditWorthinessFactorEntity) o;
+    return Objects.equals(caseId, that.caseId) &&
+            Objects.equals(customerIdentifier, that.customerIdentifier) &&
+            Objects.equals(positionInFactor, that.positionInFactor) &&
+            factorType == that.factorType;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(caseId, customerIdentifier, positionInFactor, factorType);
+  }
+}
diff --git a/service/src/main/java/io/mifos/individuallending/internal/repository/CaseParametersEntity.java b/service/src/main/java/io/mifos/individuallending/internal/repository/CaseParametersEntity.java
index 91f260f..b9ecf6b 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/repository/CaseParametersEntity.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/repository/CaseParametersEntity.java
@@ -15,16 +15,11 @@
  */
 package io.mifos.individuallending.internal.repository;
 
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.EnumType;
-import javax.persistence.Enumerated;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.Table;
+import javax.persistence.*;
 import java.math.BigDecimal;
 import java.time.temporal.ChronoUnit;
+import java.util.Objects;
+import java.util.Set;
 
 /**
  * @author Myrle Krantz
@@ -72,6 +67,9 @@
   @Column(name = "pay_cycle_align_month")
   private Integer paymentCycleAlignmentMonth;
 
+  @OneToMany(targetEntity = CaseCreditWorthinessFactorEntity.class, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "caseId")
+  private Set<CaseCreditWorthinessFactorEntity> creditWorthinessFactors;
+
   public CaseParametersEntity() {
   }
 
@@ -170,4 +168,25 @@
   public void setPaymentCycleAlignmentMonth(Integer paymentCycleAlignmentMonth) {
     this.paymentCycleAlignmentMonth = paymentCycleAlignmentMonth;
   }
+
+  public Set<CaseCreditWorthinessFactorEntity> getCreditWorthinessFactors() {
+    return creditWorthinessFactors;
+  }
+
+  public void setCreditWorthinessFactors(Set<CaseCreditWorthinessFactorEntity> creditWorthinessFactors) {
+    this.creditWorthinessFactors = creditWorthinessFactors;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    CaseParametersEntity that = (CaseParametersEntity) o;
+    return Objects.equals(caseId, that.caseId);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(caseId);
+  }
 }
diff --git a/service/src/main/java/io/mifos/individuallending/internal/repository/CreditWorthinessFactorType.java b/service/src/main/java/io/mifos/individuallending/internal/repository/CreditWorthinessFactorType.java
new file mode 100644
index 0000000..f07689c
--- /dev/null
+++ b/service/src/main/java/io/mifos/individuallending/internal/repository/CreditWorthinessFactorType.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed 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 io.mifos.individuallending.internal.repository;
+
+/**
+ * @author Myrle Krantz
+ */
+public enum CreditWorthinessFactorType {
+  INCOME_SOURCE, ASSET, DEBT
+}
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/mapper/ProductMapper.java b/service/src/main/java/io/mifos/portfolio/service/internal/mapper/ProductMapper.java
index e571632..d0fa6ed 100644
--- a/service/src/main/java/io/mifos/portfolio/service/internal/mapper/ProductMapper.java
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/mapper/ProductMapper.java
@@ -149,11 +149,12 @@
 
       return newAccountAssignmentEntity;
     }).collect(Collectors.toSet());
+    newEntity.setAccountAssignments(newAccountAssignmentEntities);
+
     newEntity.setChargeDefinitions(oldEntity.getChargeDefinitions());
     newEntity.setTaskDefinitions(oldEntity.getTaskDefinitions());
 
 
-    newEntity.setAccountAssignments(newAccountAssignmentEntities);
     return newEntity;
   }
 }
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/repository/CaseAccountAssignmentEntity.java b/service/src/main/java/io/mifos/portfolio/service/internal/repository/CaseAccountAssignmentEntity.java
index 862ab81..f6c6ff2 100644
--- a/service/src/main/java/io/mifos/portfolio/service/internal/repository/CaseAccountAssignmentEntity.java
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/repository/CaseAccountAssignmentEntity.java
@@ -16,6 +16,7 @@
 package io.mifos.portfolio.service.internal.repository;
 
 import javax.persistence.*;
+import java.util.Objects;
 
 /**
  * @author Myrle Krantz
@@ -70,4 +71,18 @@
   public void setIdentifier(String identifier) {
     this.identifier = identifier;
   }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    CaseAccountAssignmentEntity that = (CaseAccountAssignmentEntity) o;
+    return Objects.equals(caseEntity, that.caseEntity) &&
+            Objects.equals(designator, that.designator);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(caseEntity, designator);
+  }
 }
diff --git a/service/src/main/resources/db/migrations/mariadb/V1__initial_setup.sql b/service/src/main/resources/db/migrations/mariadb/V1__initial_setup.sql
index 57f544d..0c99de9 100644
--- a/service/src/main/resources/db/migrations/mariadb/V1__initial_setup.sql
+++ b/service/src/main/resources/db/migrations/mariadb/V1__initial_setup.sql
@@ -125,4 +125,18 @@
   CONSTRAINT bastet_il_cases_pk PRIMARY KEY (id),
   CONSTRAINT bastet_il_cases_uq UNIQUE (case_id),
   CONSTRAINT bastet_il_cases_par_fk FOREIGN KEY (case_id) REFERENCES bastet_cases (id)
+);
+
+CREATE TABLE bastet_il_c_credit_facts (
+  id BIGINT NOT NULL AUTO_INCREMENT,
+  case_id                  BIGINT         NOT NULL,
+  customer_identifier      VARCHAR(32)    NOT NULL,
+  position_in_factor       INT            NOT NULL,
+  position_in_customers    INT            NOT NULL,
+  fact_type                VARCHAR(32)    NOT NULL,
+  description              VARCHAR(4096)  NOT NULL,
+  amount                   DECIMAL(19,4)  NOT NULL,
+  CONSTRAINT bastet_il_c_credit_facts_pk PRIMARY KEY (id),
+  CONSTRAINT bastet_il_c_credit_facts_uq UNIQUE (case_id, customer_identifier, position_in_factor, fact_type),
+  CONSTRAINT bastet_il_c_credit_facts_par_fk FOREIGN KEY (case_id) REFERENCES bastet_il_cases (id)
 );
\ No newline at end of file