Merge pull request #40 from myrle-krantz/develop

Fixing account creation to deal with a conflict exception.
diff --git a/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java b/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java
index 2fd1429..cf92e9d 100644
--- a/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java
+++ b/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java
@@ -29,11 +29,9 @@
 import io.mifos.portfolio.api.v1.domain.*;
 import io.mifos.portfolio.api.v1.events.*;
 import io.mifos.portfolio.service.config.PortfolioServiceConfiguration;
-import io.mifos.portfolio.service.internal.util.AccountingAdapter;
 import io.mifos.portfolio.service.internal.util.RhythmAdapter;
 import org.junit.*;
 import org.junit.runner.RunWith;
-import org.mockito.Mockito;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -57,9 +55,6 @@
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doReturn;
-
 /**
  * @author Myrle Krantz
  */
@@ -86,14 +81,6 @@
     public Logger logger() {
       return LoggerFactory.getLogger(LOGGER_NAME);
     }
-    @Bean()
-    public AccountingAdapter accountingAdapter(@SuppressWarnings("SpringJavaAutowiringInspection")
-                                                   final LedgerManager ledgerManager)
-    {
-      final AccountingAdapter spy = Mockito.spy(new AccountingAdapter(ledgerManager));
-      doReturn(true).when(spy).accountAssignmentRepresentsRealAccount(any());
-      return spy;
-    }
   }
 
   static final String TEST_USER = "setau";
diff --git a/component-test/src/main/java/io/mifos/portfolio/AccountingFixture.java b/component-test/src/main/java/io/mifos/portfolio/AccountingFixture.java
index 593a999..dffbe9f 100644
--- a/component-test/src/main/java/io/mifos/portfolio/AccountingFixture.java
+++ b/component-test/src/main/java/io/mifos/portfolio/AccountingFixture.java
@@ -17,6 +17,7 @@
 
 import io.mifos.accounting.api.v1.client.LedgerManager;
 import io.mifos.accounting.api.v1.domain.*;
+import io.mifos.core.api.util.NotFoundException;
 import io.mifos.core.lang.DateConverter;
 import org.hamcrest.Description;
 import org.mockito.AdditionalMatchers;
@@ -59,6 +60,9 @@
   static final String LOAN_INTEREST_ACCRUAL_ACCOUNT_IDENTIFIER = "7810";
   static final String CONSUMER_LOAN_INTEREST_ACCOUNT_IDENTIFIER = "1103";
   static final String LOANS_PAYABLE_ACCOUNT_IDENTIFIER ="missingInChartOfAccounts";
+  static final String LATE_FEE_INCOME_ACCOUNT_IDENTIFIER = "001-008"; //TODO: ??
+  static final String LATE_FEE_ACCRUAL_ACCOUNT_IDENTIFIER = "001-009"; //TODO: ??
+  static final String ARREARS_ALLOWANCE_ACCOUNT_IDENTIFIER = "001-010"; //TODO: ??
 
   static final Map<String, AccountData> accountMap = new HashMap<>();
 
@@ -88,8 +92,8 @@
     final AccountData accountData = new AccountData(account);
     accountMap.put(account.getIdentifier(), accountData);
     Mockito.doAnswer(new AccountEntriesStreamAnswer(accountData))
-            .when(ledgerManagerMock)
-            .fetchAccountEntriesStream(Mockito.eq(account.getIdentifier()), Matchers.anyString(), Matchers.anyString(), Matchers.eq("ASC"));
+        .when(ledgerManagerMock)
+        .fetchAccountEntriesStream(Mockito.eq(account.getIdentifier()), Matchers.anyString(), Matchers.anyString(), Matchers.eq("ASC"));
   }
 
 
@@ -221,6 +225,30 @@
     return ret;
   }
 
+  private static Account lateFeeIncomeAccount() {
+    final Account ret = new Account();
+    ret.setIdentifier(LATE_FEE_INCOME_ACCOUNT_IDENTIFIER);
+    //ret.setLedger(LOAN_INCOME_LEDGER_IDENTIFIER); //TODO: ??
+    ret.setType(AccountType.LIABILITY.name()); //TODO: ??
+    return ret;
+  }
+
+  private static Account lateFeeAccrualAccount() {
+    final Account ret = new Account();
+    ret.setIdentifier(LATE_FEE_ACCRUAL_ACCOUNT_IDENTIFIER);
+    //ret.setLedger(LOAN_INCOME_LEDGER_IDENTIFIER); //TODO: ??
+    ret.setType(AccountType.LIABILITY.name()); //TODO: ??
+    return ret;
+  }
+
+  private static Account arrearsAllowanceAccount() {
+    final Account ret = new Account();
+    ret.setIdentifier(ARREARS_ALLOWANCE_ACCOUNT_IDENTIFIER);
+    //ret.setLedger(LOAN_INCOME_LEDGER_IDENTIFIER); //TODO: ??
+    ret.setType(AccountType.LIABILITY.name()); //TODO: ??
+    return ret;
+  }
+
   private static AccountPage customerLoanAccountsPage() {
     final Account customerLoanAccount1 = new Account();
     customerLoanAccount1.setIdentifier("customerLoanAccount1");
@@ -363,7 +391,11 @@
     @Override
     public Account answer(final InvocationOnMock invocation) throws Throwable {
       final String identifier = invocation.getArgumentAt(0, String.class);
-      return accountMap.get(identifier).account;
+      final AccountData ret = accountMap.get(identifier);
+      if (ret != null)
+        return ret.account;
+      else
+        throw new NotFoundException("Account '" + identifier + "' not found.");
     }
   }
 
@@ -402,6 +434,9 @@
     makeAccountResponsive(loanInterestAccrualAccount(), universalCreationDate, ledgerManagerMock);
     makeAccountResponsive(consumerLoanInterestAccount(), universalCreationDate, ledgerManagerMock);
     makeAccountResponsive(loansPayableAccount(), universalCreationDate, ledgerManagerMock);
+    makeAccountResponsive(lateFeeIncomeAccount(), universalCreationDate, ledgerManagerMock);
+    makeAccountResponsive(lateFeeAccrualAccount(), universalCreationDate, ledgerManagerMock);
+    makeAccountResponsive(arrearsAllowanceAccount(), universalCreationDate, ledgerManagerMock);
 
     Mockito.doReturn(incomeLedger()).when(ledgerManagerMock).findLedger(INCOME_LEDGER_IDENTIFIER);
     Mockito.doReturn(feesAndChargesLedger()).when(ledgerManagerMock).findLedger(FEES_AND_CHARGES_LEDGER_IDENTIFIER);
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 5910a20..777a327 100644
--- a/component-test/src/main/java/io/mifos/portfolio/Fixture.java
+++ b/component-test/src/main/java/io/mifos/portfolio/Fixture.java
@@ -69,9 +69,9 @@
     accountAssignments.add(new AccountAssignment(INTEREST_INCOME, CONSUMER_LOAN_INTEREST_ACCOUNT_IDENTIFIER));
     accountAssignments.add(new AccountAssignment(INTEREST_ACCRUAL, LOAN_INTEREST_ACCRUAL_ACCOUNT_IDENTIFIER));
     accountAssignments.add(new AccountAssignment(LOANS_PAYABLE, LOANS_PAYABLE_ACCOUNT_IDENTIFIER));
-    accountAssignments.add(new AccountAssignment(LATE_FEE_INCOME, "001-008"));
-    accountAssignments.add(new AccountAssignment(LATE_FEE_ACCRUAL, "001-009"));
-    accountAssignments.add(new AccountAssignment(ARREARS_ALLOWANCE, "001-010"));
+    accountAssignments.add(new AccountAssignment(LATE_FEE_INCOME, LATE_FEE_INCOME_ACCOUNT_IDENTIFIER));
+    accountAssignments.add(new AccountAssignment(LATE_FEE_ACCRUAL, LATE_FEE_ACCRUAL_ACCOUNT_IDENTIFIER));
+    accountAssignments.add(new AccountAssignment(ARREARS_ALLOWANCE, ARREARS_ALLOWANCE_ACCOUNT_IDENTIFIER));
     //accountAssignments.add(new AccountAssignment(ENTRY, ...));
     // Don't assign entry account in test since it usually will not be assigned IRL.
     accountAssignments.add(new AccountAssignment(LOAN_FUNDS_SOURCE, LOAN_FUNDS_SOURCE_ACCOUNT_IDENTIFIER));
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/util/AccountingAdapter.java b/service/src/main/java/io/mifos/portfolio/service/internal/util/AccountingAdapter.java
index ad6a55d..341d2d7 100644
--- a/service/src/main/java/io/mifos/portfolio/service/internal/util/AccountingAdapter.java
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/util/AccountingAdapter.java
@@ -15,6 +15,7 @@
  */
 package io.mifos.portfolio.service.internal.util;
 
+import io.mifos.accounting.api.v1.client.AccountAlreadyExistsException;
 import io.mifos.accounting.api.v1.client.AccountNotFoundException;
 import io.mifos.accounting.api.v1.client.LedgerManager;
 import io.mifos.accounting.api.v1.client.LedgerNotFoundException;
@@ -25,8 +26,11 @@
 import io.mifos.core.lang.ServiceException;
 import io.mifos.portfolio.api.v1.domain.AccountAssignment;
 import io.mifos.portfolio.api.v1.domain.ChargeDefinition;
+import io.mifos.portfolio.service.ServiceConstants;
 import org.apache.commons.lang.RandomStringUtils;
+import org.slf4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Component;
 
 import java.math.BigDecimal;
@@ -48,10 +52,13 @@
   public enum IdentifierType {LEDGER, ACCOUNT}
 
   private final LedgerManager ledgerManager;
+  private final Logger logger;
 
   @Autowired
-  public AccountingAdapter(@SuppressWarnings("SpringJavaAutowiringInspection") final LedgerManager ledgerManager) {
+  public AccountingAdapter(@SuppressWarnings("SpringJavaAutowiringInspection") final LedgerManager ledgerManager,
+                           @Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger) {
     this.ledgerManager = ledgerManager;
+    this.logger = logger;
   }
 
   public void bookCharges(final List<ChargeInstance> costComponents,
@@ -155,14 +162,33 @@
     generatedAccount.setBalance(0.0);
     generatedAccount.setType(ledger.getType());
     generatedAccount.setState(Account.State.OPEN.name());
-    final String accountNumber = customerIdentifier + "." + ledgerAssignment.getDesignator()
-            + "." + String.format("%05d", accountsOfLedger.getTotalElements() + 1);
-    generatedAccount.setIdentifier(accountNumber);
+    long guestimatedAccountIndex = accountsOfLedger.getTotalElements() + 1;
     generatedAccount.setLedger(ledger.getIdentifier());
-    generatedAccount.setName(accountNumber);
-    ledgerManager.createAccount(generatedAccount);
+    final Optional<String> createdAccountNumber =
+        Stream.iterate(guestimatedAccountIndex, i -> i + 1).limit(99999 - guestimatedAccountIndex)
+        .map(i -> {
+          final String accountNumber = createAccountNumber(customerIdentifier, ledgerAssignment.getDesignator(), i);
+          generatedAccount.setIdentifier(accountNumber);
+          generatedAccount.setName(accountNumber);
+          try {
+            ledgerManager.createAccount(generatedAccount);
+            return Optional.of(accountNumber);
+          } catch (final AccountAlreadyExistsException e) {
+            logger.error("Account '{}' could not be created because it already exists.", accountNumber);
+            return Optional.<String>empty();
+          }
+        })
+        .filter(Optional::isPresent).map(Optional::get)
+        .findFirst();
 
-    return accountNumber;
+    return createdAccountNumber.orElseThrow(() ->
+        ServiceException.conflict("Failed to create an account for customer ''{0}'' and ''{1}'', in ledger ''{2}''.",
+            customerIdentifier, ledgerAssignment.getDesignator(), ledgerAssignment.getLedgerIdentifier()));
+  }
+
+  private String createAccountNumber(final String customerIdentifier, final String designator, final long accountIndex) {
+    return customerIdentifier + "." + designator
+            + "." + String.format("%05d", accountIndex);
   }