Merge pull request #12 from myrle-krantz/develop

First steps towards automatic, scheduled, calculation of interest.
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 a2eaf37..0758a33 100644
--- a/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java
+++ b/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java
@@ -30,8 +30,9 @@
 import io.mifos.portfolio.api.v1.domain.Product;
 import io.mifos.portfolio.api.v1.events.CaseEvent;
 import io.mifos.portfolio.api.v1.events.EventConstants;
-import io.mifos.portfolio.service.PortfolioServiceConfiguration;
+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.rules.RuleChain;
 import org.junit.rules.TestRule;
@@ -41,6 +42,7 @@
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
 import org.springframework.cloud.netflix.feign.EnableFeignClients;
 import org.springframework.cloud.netflix.ribbon.RibbonClient;
 import org.springframework.context.annotation.Bean;
@@ -84,7 +86,8 @@
       return LoggerFactory.getLogger("test-logger");
     }
     @Bean()
-    public AccountingAdapter accountingAdapter(final LedgerManager ledgerManager)
+    public AccountingAdapter accountingAdapter(@SuppressWarnings("SpringJavaAutowiringInspection")
+                                                   final LedgerManager ledgerManager)
     {
       final AccountingAdapter spy = Mockito.spy(new AccountingAdapter(ledgerManager));
       doReturn(true).when(spy).accountAssignmentRepresentsRealAccount(any());
@@ -121,6 +124,10 @@
   @Autowired
   IndividualLending individualLending;
 
+  @SuppressWarnings("unused")
+  @MockBean
+  RhythmAdapter rhythmAdapter;
+
   @Before
   public void prepTest() {
     userContext = this.tenantApplicationSecurityEnvironment.createAutoUserContext(TEST_USER);
diff --git a/service/build.gradle b/service/build.gradle
index e5a945a..f4f79d6 100644
--- a/service/build.gradle
+++ b/service/build.gradle
@@ -33,6 +33,7 @@
             [group: 'org.springframework.boot', name: 'spring-boot-starter-jetty'],
             [group: 'io.mifos.portfolio', name: 'api', version: project.version],
             [group: 'io.mifos.rhythm', name: 'spi', version: versions.mifosrhythm],
+            [group: 'io.mifos.rhythm', name: 'api', version: versions.mifosrhythm],
             [group: 'io.mifos.accounting', name: 'api', version: versions.frameworkaccounting],
             [group: 'io.mifos.anubis', name: 'library', version: versions.frameworkanubis],
             [group: 'com.google.code.gson', name: 'gson'],
diff --git a/service/src/main/java/io/mifos/portfolio/service/PortfolioApplication.java b/service/src/main/java/io/mifos/portfolio/service/PortfolioApplication.java
index 5c556e5..58e0209 100644
--- a/service/src/main/java/io/mifos/portfolio/service/PortfolioApplication.java
+++ b/service/src/main/java/io/mifos/portfolio/service/PortfolioApplication.java
@@ -15,6 +15,7 @@
  */
 package io.mifos.portfolio.service;
 
+import io.mifos.portfolio.service.config.PortfolioServiceConfiguration;
 import org.springframework.boot.SpringApplication;
 
 public class PortfolioApplication {
diff --git a/service/src/main/java/io/mifos/portfolio/service/config/PortfolioProperties.java b/service/src/main/java/io/mifos/portfolio/service/config/PortfolioProperties.java
new file mode 100644
index 0000000..98936f6
--- /dev/null
+++ b/service/src/main/java/io/mifos/portfolio/service/config/PortfolioProperties.java
@@ -0,0 +1,44 @@
+/*
+ * 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.portfolio.service.config;
+
+import io.mifos.core.lang.validation.constraints.ValidIdentifier;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+import org.springframework.validation.annotation.Validated;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings("unused")
+@Component
+@ConfigurationProperties(prefix="portfolio")
+@Validated
+public class PortfolioProperties {
+  @ValidIdentifier
+  private String bookInterestAsUser;
+
+  public PortfolioProperties() {
+  }
+
+  public String getBookInterestAsUser() {
+    return bookInterestAsUser;
+  }
+
+  public void setBookInterestAsUser(String bookInterestAsUser) {
+    this.bookInterestAsUser = bookInterestAsUser;
+  }
+}
diff --git a/service/src/main/java/io/mifos/portfolio/service/PortfolioServiceConfiguration.java b/service/src/main/java/io/mifos/portfolio/service/config/PortfolioServiceConfiguration.java
similarity index 87%
rename from service/src/main/java/io/mifos/portfolio/service/PortfolioServiceConfiguration.java
rename to service/src/main/java/io/mifos/portfolio/service/config/PortfolioServiceConfiguration.java
index f9838b7..adcb3b3 100644
--- a/service/src/main/java/io/mifos/portfolio/service/PortfolioServiceConfiguration.java
+++ b/service/src/main/java/io/mifos/portfolio/service/config/PortfolioServiceConfiguration.java
@@ -13,17 +13,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.mifos.portfolio.service;
+package io.mifos.portfolio.service.config;
 
 import com.google.gson.Gson;
+import io.mifos.accounting.api.v1.client.LedgerManager;
 import io.mifos.anubis.config.EnableAnubis;
-import io.mifos.individuallending.IndividualLendingConfiguration;
 import io.mifos.core.async.config.EnableAsync;
 import io.mifos.core.cassandra.config.EnableCassandra;
 import io.mifos.core.command.config.EnableCommandProcessing;
+import io.mifos.core.lang.config.EnableApplicationName;
 import io.mifos.core.lang.config.EnableServiceException;
 import io.mifos.core.lang.config.EnableTenantContext;
 import io.mifos.core.mariadb.config.EnableMariaDB;
+import io.mifos.individuallending.IndividualLendingConfiguration;
+import io.mifos.portfolio.service.ServiceConstants;
+import io.mifos.rhythm.api.v1.client.RhythmManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -52,10 +56,13 @@
 @ComponentScan({
     "io.mifos.portfolio.service.rest",
     "io.mifos.portfolio.service.internal",
+    "io.mifos.portfolio.service.config",
 })
 @EnableJpaRepositories(basePackages = "io.mifos.portfolio.service.internal.repository")
 @EntityScan(basePackages = "io.mifos.portfolio.service.internal.repository")
-@EnableFeignClients(basePackages = {"io.mifos.accounting.api.v1"})
+@EnableFeignClients(clients = {LedgerManager.class, RhythmManager.class})
+@RibbonClient(name = "portfolio-v1")
+@EnableApplicationName
 @Import(IndividualLendingConfiguration.class)
 public class PortfolioServiceConfiguration extends WebMvcConfigurerAdapter {
 
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/BeatPublishCommandHandler.java b/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/BeatPublishCommandHandler.java
index b4047f1..bfa9e56 100644
--- a/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/BeatPublishCommandHandler.java
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/BeatPublishCommandHandler.java
@@ -1,3 +1,18 @@
+/*
+ * 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.portfolio.service.internal.command.handler;
 
 import io.mifos.core.command.annotation.Aggregate;
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/InitializeCommandHandler.java b/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/InitializeCommandHandler.java
index 454b40f..ba049a5 100644
--- a/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/InitializeCommandHandler.java
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/InitializeCommandHandler.java
@@ -22,6 +22,7 @@
 import io.mifos.core.command.annotation.CommandHandler;
 import io.mifos.core.command.annotation.EventEmitter;
 import io.mifos.core.mariadb.domain.FlywayFactoryBean;
+import io.mifos.portfolio.service.internal.util.RhythmAdapter;
 import org.slf4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
@@ -37,15 +38,18 @@
   private final Logger logger;
   private final DataSource dataSource;
   private final FlywayFactoryBean flywayFactoryBean;
+  private final RhythmAdapter rhythmAdapter;
 
   @Autowired
   public InitializeCommandHandler(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
-                            final DataSource dataSource,
-                            final FlywayFactoryBean flywayFactoryBean) {
+                                  final DataSource dataSource,
+                                  final FlywayFactoryBean flywayFactoryBean,
+                                  final RhythmAdapter rhythmAdapter) {
     super();
     this.logger = logger;
     this.dataSource = dataSource;
     this.flywayFactoryBean = flywayFactoryBean;
+    this.rhythmAdapter = rhythmAdapter;
   }
 
   @CommandHandler
@@ -53,6 +57,7 @@
   public String initialize(final InitializeServiceCommand initializeServiceCommand) {
     this.logger.debug("Start service migration.");
     this.flywayFactoryBean.create(this.dataSource).migrate();
+    rhythmAdapter.request24Beats();
     return EventConstants.INITIALIZE;
   }
 }
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 4f2c59a..9d33487 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
@@ -18,8 +18,6 @@
 import io.mifos.accounting.api.v1.client.AccountNotFoundException;
 import io.mifos.accounting.api.v1.client.LedgerManager;
 import io.mifos.accounting.api.v1.client.LedgerNotFoundException;
-import io.mifos.accounting.api.v1.domain.Account;
-import io.mifos.accounting.api.v1.domain.Ledger;
 import io.mifos.portfolio.api.v1.domain.AccountAssignment;
 import io.mifos.portfolio.api.v1.domain.ChargeDefinition;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -42,7 +40,7 @@
   private final LedgerManager ledgerManager;
 
   @Autowired
-  public AccountingAdapter(final LedgerManager ledgerManager) {
+  public AccountingAdapter(@SuppressWarnings("SpringJavaAutowiringInspection") final LedgerManager ledgerManager) {
     this.ledgerManager = ledgerManager;
   }
 
@@ -72,7 +70,7 @@
   public boolean accountAssignmentRepresentsRealAccount(final AccountAssignment accountAssignment) {
     if (accountAssignment.getAccountIdentifier() != null) {
       try {
-        final Account account = ledgerManager.findAccount(accountAssignment.getAccountIdentifier());
+        ledgerManager.findAccount(accountAssignment.getAccountIdentifier());
         return true;
       }
       catch (final AccountNotFoundException e){
@@ -81,7 +79,7 @@
     }
     else if (accountAssignment.getLedgerIdentifier() != null) {
       try {
-        final Ledger ledger = ledgerManager.findLedger(accountAssignment.getLedgerIdentifier());
+        ledgerManager.findLedger(accountAssignment.getLedgerIdentifier());
         return true;
       }
       catch (final LedgerNotFoundException e){
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/util/RhythmAdapter.java b/service/src/main/java/io/mifos/portfolio/service/internal/util/RhythmAdapter.java
new file mode 100644
index 0000000..0d9716f
--- /dev/null
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/util/RhythmAdapter.java
@@ -0,0 +1,67 @@
+/*
+ * 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.portfolio.service.internal.util;
+
+import io.mifos.core.lang.ApplicationName;
+import io.mifos.portfolio.service.ServiceConstants;
+import io.mifos.rhythm.api.v1.client.RhythmManager;
+import io.mifos.rhythm.api.v1.domain.Beat;
+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.util.stream.Stream;
+
+/**
+ * @author Myrle Krantz
+ */
+@Component
+public class RhythmAdapter {
+  final private RhythmManager rhythmManager;
+  final private ApplicationName applicationName;
+  final private Logger logger;
+  @Autowired
+  public RhythmAdapter(@SuppressWarnings("SpringJavaAutowiringInspection") final RhythmManager rhythmManager,
+                       final ApplicationName applicationName,
+                       final @Qualifier(ServiceConstants.LOGGER_NAME)Logger logger) {
+    this.rhythmManager = rhythmManager;
+    this.applicationName = applicationName;
+    this.logger = logger;
+  }
+
+  public void request24Beats() {
+    Stream.iterate(0, x -> x+1).limit(24)
+            .map(RhythmAdapter::defineBeat)
+            .forEach(this::createBeat);
+  }
+
+  private static Beat defineBeat(final int alignmentHour) {
+    final Beat beat = new Beat();
+    beat.setAlignmentHour(alignmentHour);
+    beat.setIdentifier("alignment" + alignmentHour);
+    return beat;
+  }
+
+  private void createBeat(final Beat beat) {
+    try {
+      rhythmManager.createBeat(applicationName.toString(), beat);
+    }
+    catch (final Exception e) {
+      logger.error("Creating interest calculation beat {} failed with exception e.", beat, e);
+    }
+  }
+}
\ No newline at end of file
diff --git a/service/src/main/resources/application.yml b/service/src/main/resources/application.yml
index d50619f..a15f8b7 100644
--- a/service/src/main/resources/application.yml
+++ b/service/src/main/resources/application.yml
@@ -80,4 +80,7 @@
 system:
   publicKey:
     exponent: 65537
-    modulus: 21188023007955682867939457181271038457216099278949187456460742046123672432355777599460689470319454021384777684967830053993002724303461144745107517305075315187397862430851722919529943465029389248042840364475999768651348557757734298942211509744303551097953258597691851996692366468761965138767429272032120029271744611798874201312092155969603381492096789028306859853929900848124928201000469425135976322303229632628092728624143573273277870884919055453251617011673264035045823652246768583219018126865521694880333238485410601803458379987829318615730229086183405850999386270584135805252231189505197494383178133769189765423639
\ No newline at end of file
+    modulus: 21188023007955682867939457181271038457216099278949187456460742046123672432355777599460689470319454021384777684967830053993002724303461144745107517305075315187397862430851722919529943465029389248042840364475999768651348557757734298942211509744303551097953258597691851996692366468761965138767429272032120029271744611798874201312092155969603381492096789028306859853929900848124928201000469425135976322303229632628092728624143573273277870884919055453251617011673264035045823652246768583219018126865521694880333238485410601803458379987829318615730229086183405850999386270584135805252231189505197494383178133769189765423639
+
+portfolio:
+  bookInterestAsUser: interest_user
\ No newline at end of file