Merge branch 'develop' into FINCN-124
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..a3b6dc5
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,7 @@
+LICENSE
+README.md
+docs/
+HEADER
+NOTICE.txt
+.git/
+.gitignore
diff --git a/.gitignore b/.gitignore
index 480c856..5b0038d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,10 @@
.idea
build/
target/
-**/out
+
+api/out/
+component-test/out/
+service/out
# Ignore Gradle GUI config
gradle-app.setting
@@ -15,3 +18,5 @@
*.log
*.toDelete
+
+*.jar
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..2f484c8
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,33 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF 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.
+#
+language: java
+sudo: false
+jdk:
+- openjdk8
+install: true
+script: "./travis.sh"
+env:
+ global:
+ - BUILD_SNAPSHOTS_BRANCH=develop
+ - ARTIFACTORY_URL=https://mifos.jfrog.io/mifos
+ - ARTIFACTORY_USER=travis-ci
+ - secure: "Q+gO7X7kludIHuAI/VIiI60olEHEkUVVPT0atuf9GceT6IaKjWkdO2w3uwltNpedGcNX07bA2P/5ELxCkiL+maCbxXED2u/+zZfvKenUzRsDXkaZ4PjX9T0poX+cai4yO4uEdUSq5qmVun7JN4KB5hQq96yMditRtHb4LWM28r6owRykQ1aRNGsWlYhFTni63HSICyZFpuWc1/7N6ClEJYiJRwkNG2NFHpqi9CkpX9YgIoJgx7226RBMm6azRek5BAMNWsRYn7iKDcL6XakWUrXGPTBM37Cg75ClCrweXwi9J1d7O7gEYrqhPgzzKWpg4vrnaZwRhGV1esAhXKUuHyMyfM1UTez3zRancNJj8SmdXPHCU45vc6r5qdJTzt8GCyJGxIe0Wh/X/8j/4oMC7vBOELqIINU0V/aopFAeutrUxacdl91qF7iV6xZBxLmrSJeRMLL/d4LENFysSozfH1E50rWxnou/fm2acjl/6uyCoe4IcA7hUBgnJhd6GRwkg/Uu/Y7HmFBcvMUveziFG/Dy8zlNZBuXRbdbwkGpnFtxjRADw97VkiRpI+yn+uisXbUN//t7yWgxEO3MovUylNdRjsNHE/VzVz5Q3UZihL9NUqY8pmWbV2zZ85lSgTVlp0gY6M+94y4WO8La2yzW8h9rbsCzdGlX5FVtsW0jKr4="
+after_failure:
+ - ./gradlew rat
+ - cat api/build/reports/rat/rat-report.txt
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..3ee37c8
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,37 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF 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.
+#
+FROM openjdk:8-jdk-alpine AS builder
+RUN mkdir builddir
+COPY . builddir
+WORKDIR builddir
+RUN ./gradlew publishToMavenLocal
+
+FROM openjdk:8-jdk-alpine AS runner
+
+ARG portfolio_port=2026
+
+ENV server.max-http-header-size=16384 \
+ cassandra.clusterName="Test Cluster" \
+ portfolio.bookLateFeesAndInterestAsUser=imhotep \
+ server.port=$portfolio_port
+
+WORKDIR /tmp
+COPY --from=builder /builddir/service/build/libs/service-0.1.0-BUILD-SNAPSHOT-boot.jar ./portfolio-service-boot.jar
+
+CMD ["java", "-jar", "portfolio-service-boot.jar"]
diff --git a/README.md b/README.md
index 6d41c15..78c9c6a 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Apache Fineract CN Portfolio
+# Apache Fineract CN Portfolio [![Build Status](https://api.travis-ci.com/apache/fineract-cn-portfolio.svg?branch=develop)](https://travis-ci.com/apache/fineract-cn-portfolio)
This project provides product and case management for Fineract CN. Products are described and "instantiated"
for customers as cases.
diff --git a/api/build.gradle b/api/build.gradle
index 421c480..5e0f91d 100644
--- a/api/build.gradle
+++ b/api/build.gradle
@@ -30,6 +30,7 @@
plugins {
id 'com.github.hierynomus.license' version '0.13.1'
id("org.nosphere.apache.rat") version "0.3.1"
+ id "com.jfrog.artifactory" version "4.9.5"
}
apply from: '../shared.gradle'
@@ -55,7 +56,7 @@
from components.java
groupId project.group
artifactId project.name
- version project.version
+ version project.findProperty('externalVersion') ?: project.version
}
}
}
diff --git a/build.gradle b/build.gradle
index 18918cd..eeb68fb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -39,6 +39,14 @@
dependsOn publishComponentTestToMavenLocal
}
+task artifactoryPublish {
+ group 'all'
+ dependsOn publishToMavenLocal
+ dependsOn gradle.includedBuild('api').task(':artifactoryPublish')
+ dependsOn gradle.includedBuild('service').task(':artifactoryPublish')
+ dependsOn gradle.includedBuild('component-test').task(':artifactoryPublish')
+}
+
task prepareForTest {
group 'all'
dependsOn publishToMavenLocal
@@ -57,4 +65,4 @@
dependsOn gradle.includedBuild('api').task(':rat')
dependsOn gradle.includedBuild('service').task(':rat')
dependsOn gradle.includedBuild('component-test').task(':rat')
-}
\ No newline at end of file
+}
diff --git a/component-test/build.gradle b/component-test/build.gradle
index b0dd2d7..266d54f 100644
--- a/component-test/build.gradle
+++ b/component-test/build.gradle
@@ -26,16 +26,22 @@
dependencies {
classpath ("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
+ classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.2'
}
}
plugins {
id 'com.github.hierynomus.license' version '0.13.1'
id("org.nosphere.apache.rat") version "0.3.1"
+ id "com.jfrog.artifactory" version "4.9.5"
}
apply from: '../shared.gradle'
+apply plugin: 'spring-boot'
+apply plugin: 'org.asciidoctor.convert'
+
+
dependencies {
compile(
[group: 'org.springframework.cloud', name: 'spring-cloud-starter-eureka-server'],
@@ -46,14 +52,26 @@
[group: 'org.apache.fineract.cn', name: 'api', version: versions.frameworkapi],
[group: 'org.apache.fineract.cn', name: 'test', version: versions.frameworktest],
[group: 'org.apache.fineract.cn', name: 'lang', version: versions.frameworklang],
- [group: 'org.springframework.boot', name: 'spring-boot-starter-test']
+ [group: 'org.springframework.boot', name: 'spring-boot-starter-test'],
+ [group: 'org.springframework.restdocs', name: 'spring-restdocs-mockmvc'],
+ [group: 'junit', name: 'junit', version: '4.12']
)
+
+}
+
+asciidoctor {
+ sourceDir 'build/doc/asciidoc/'
+ outputDir 'build/doc/html5'
+ options backend: "html", doctype: "book"
+ attributes "source-highlighter": "highlightjs", \
+ 'snippets': file('build/doc/generated-snippets/')
}
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
+ version project.findProperty('externalVersion') ?: project.version
}
}
}
diff --git a/component-test/src/main/java/org/apache/fineract/cn/portfolio/BalanceSegmentSetApiDocumentation.java b/component-test/src/main/java/org/apache/fineract/cn/portfolio/BalanceSegmentSetApiDocumentation.java
new file mode 100644
index 0000000..310e638
--- /dev/null
+++ b/component-test/src/main/java/org/apache/fineract/cn/portfolio/BalanceSegmentSetApiDocumentation.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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.apache.fineract.cn.portfolio;
+
+import com.google.gson.Gson;
+import org.apache.fineract.cn.portfolio.api.v1.domain.BalanceSegmentSet;
+import org.apache.fineract.cn.portfolio.api.v1.domain.Product;
+import org.apache.fineract.cn.portfolio.api.v1.events.BalanceSegmentSetEvent;
+import org.apache.fineract.cn.portfolio.api.v1.events.EventConstants;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.restdocs.JUnitRestDocumentation;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import java.math.BigDecimal;
+import java.util.Arrays;
+
+import static org.apache.fineract.cn.lang.config.TenantHeaderFilter.TENANT_HEADER;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.*;
+import static org.springframework.restdocs.payload.PayloadDocumentation.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+public class BalanceSegmentSetApiDocumentation extends AbstractPortfolioTest {
+
+ @Rule
+ public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("build/doc/generated-snippets/test-balancesegmentsets");
+
+ @Autowired
+ private WebApplicationContext context;
+
+ private MockMvc mockMvc;
+
+ @Before
+ public void setUp() {
+
+ this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
+ .apply(documentationConfiguration(this.restDocumentation))
+ .alwaysDo(document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
+ .build();
+ }
+
+ @Test
+ public void documentCreateBalanceSegmentSet() throws Exception {
+
+ final Product product = createProduct();
+
+ final BalanceSegmentSet balanceSegmentSet = new BalanceSegmentSet();
+ balanceSegmentSet.setIdentifier(testEnvironment.generateUniqueIdentifier("bss"));
+ balanceSegmentSet.setSegments(Arrays.asList(
+ BigDecimal.ZERO.setScale(5, BigDecimal.ROUND_HALF_EVEN),
+ BigDecimal.TEN.setScale(5, BigDecimal.ROUND_HALF_EVEN),
+ BigDecimal.valueOf(10_000_0000, 5)));
+ balanceSegmentSet.setSegmentIdentifiers(Arrays.asList("abc", "def", "ghi"));
+
+ Gson gson = new Gson();
+ this.mockMvc.perform(post("/products/" + product.getIdentifier() + "/balancesegmentsets/")
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .content(gson.toJson(balanceSegmentSet)))
+ .andExpect(status().isAccepted())
+ .andDo(document("document-create-balance-segment-set", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()),
+ requestFields(
+ fieldWithPath("identifier").description("Balance segment set's identifier"),
+ fieldWithPath("segments").description("Balance segment set's given name"),
+ fieldWithPath("segmentIdentifiers").description("Balance segment sets's segment identfiers"))));
+
+ }
+
+ @Test
+ public void documentGetAllBalanceSegmentSets() throws Exception {
+ final Product product = createProduct();
+
+ final BalanceSegmentSet balanceSegmentSet = new BalanceSegmentSet();
+ balanceSegmentSet.setIdentifier(testEnvironment.generateUniqueIdentifier("bss"));
+ balanceSegmentSet.setSegments(Arrays.asList(
+ BigDecimal.ZERO.setScale(3, BigDecimal.ROUND_HALF_EVEN),
+ BigDecimal.TEN.setScale(3, BigDecimal.ROUND_HALF_EVEN),
+ BigDecimal.valueOf(10_000_0000, 3)));
+ balanceSegmentSet.setSegmentIdentifiers(Arrays.asList("how", "are", "you"));
+
+ portfolioManager.createBalanceSegmentSet(product.getIdentifier(), balanceSegmentSet);
+
+ this.mockMvc.perform(get("/products/" + product.getIdentifier() + "/balancesegmentsets/")
+ .accept(MediaType.APPLICATION_JSON_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header(TENANT_HEADER, tenantDataStoreContext.getTenantName()))
+ .andExpect(status().isOk())
+ .andDo(document(
+ "document-get-all-balance-segment-sets", preprocessRequest(prettyPrint()),
+ responseFields(
+ fieldWithPath("[]").description("An array of balance segment sets"),
+ fieldWithPath("[].identifier").description("Balance segment set's identifier "),
+ fieldWithPath("[].segments").description("Balance segment set's segments "),
+ fieldWithPath("[].segmentIdentifiers").description("Balance segment set's segment identifier"))));
+
+ }
+
+ @Test
+ public void documentGetBalanceSegmentSet() throws Exception {
+ final Product product = createProduct();
+
+ final BalanceSegmentSet balanceSegmentSet = new BalanceSegmentSet();
+ balanceSegmentSet.setIdentifier(testEnvironment.generateUniqueIdentifier("bss"));
+ balanceSegmentSet.setSegments(Arrays.asList(
+ BigDecimal.ZERO.setScale(3, BigDecimal.ROUND_HALF_EVEN),
+ BigDecimal.TEN.setScale(3, BigDecimal.ROUND_HALF_EVEN),
+ BigDecimal.valueOf(10_000_0000, 3)));
+ balanceSegmentSet.setSegmentIdentifiers(Arrays.asList("how", "are", "you"));
+
+ portfolioManager.createBalanceSegmentSet(product.getIdentifier(), balanceSegmentSet);
+ this.eventRecorder.wait(EventConstants.POST_BALANCE_SEGMENT_SET, new BalanceSegmentSetEvent(product.getIdentifier(), balanceSegmentSet.getIdentifier()));
+
+
+ this.mockMvc.perform(get("/products/" + product.getIdentifier() + "/balancesegmentsets/" + balanceSegmentSet.getIdentifier())
+ .accept(MediaType.APPLICATION_JSON_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header(TENANT_HEADER, tenantDataStoreContext.getTenantName()))
+ .andExpect(status().isOk())
+ .andDo(document(
+ "document-get-balance-segment-set", preprocessRequest(prettyPrint()),
+ responseFields(
+ fieldWithPath("identifier").description("Balance segment set's identifier"),
+ fieldWithPath("segments").description("Balance segment set's given name"),
+ fieldWithPath("segmentIdentifiers").description("Balance segment sets's segment identfiers"))));
+
+ }
+
+ @Test
+ public void documentChangeBalanceSegmentSet() throws Exception {
+ final Product product = createProduct();
+
+ final BalanceSegmentSet balanceSegmentSet = new BalanceSegmentSet();
+ balanceSegmentSet.setIdentifier(testEnvironment.generateUniqueIdentifier("bss"));
+ balanceSegmentSet.setSegments(Arrays.asList(
+ BigDecimal.ZERO.setScale(3, BigDecimal.ROUND_HALF_EVEN),
+ BigDecimal.TEN.setScale(3, BigDecimal.ROUND_HALF_EVEN),
+ BigDecimal.valueOf(10_000_0000, 3)));
+ balanceSegmentSet.setSegmentIdentifiers(Arrays.asList("how", "are", "you"));
+
+ portfolioManager.createBalanceSegmentSet(product.getIdentifier(), balanceSegmentSet);
+ this.eventRecorder.wait(EventConstants.POST_BALANCE_SEGMENT_SET, new BalanceSegmentSetEvent(product.getIdentifier(), balanceSegmentSet.getIdentifier()));
+
+
+ balanceSegmentSet.setSegments(Arrays.asList(
+ BigDecimal.ZERO.setScale(6, BigDecimal.ROUND_HALF_EVEN),
+ BigDecimal.valueOf(100_0000, 6),
+ BigDecimal.valueOf(10_000_0000, 6)));
+ balanceSegmentSet.setSegmentIdentifiers(Arrays.asList("abc", "def", "ghi"));
+
+ Gson gson = new Gson();
+ this.mockMvc.perform(put("/products/" + product.getIdentifier() + "/balancesegmentsets/" + balanceSegmentSet.getIdentifier())
+ .accept(MediaType.APPLICATION_JSON_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header(TENANT_HEADER, tenantDataStoreContext.getTenantName())
+ .content(gson.toJson(balanceSegmentSet)))
+ .andExpect(status().isAccepted())
+ .andDo(document(
+ "document-change-balance-segment-set", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()),
+ requestFields(
+ fieldWithPath("identifier").description("Balance segment set's identifier"),
+ fieldWithPath("segments").description("Balance segment set's given name"),
+ fieldWithPath("segmentIdentifiers").description("Balance segment sets's segment identfiers"))));
+
+ }
+
+ @Test
+ public void documentDeleteBalanceSegmentSet() throws Exception {
+ final Product product = createProduct();
+
+ final BalanceSegmentSet balanceSegmentSet = new BalanceSegmentSet();
+ balanceSegmentSet.setIdentifier(testEnvironment.generateUniqueIdentifier("bss"));
+ balanceSegmentSet.setSegments(Arrays.asList(
+ BigDecimal.ZERO.setScale(3, BigDecimal.ROUND_HALF_EVEN),
+ BigDecimal.TEN.setScale(3, BigDecimal.ROUND_HALF_EVEN),
+ BigDecimal.valueOf(10_000_0000, 3)));
+ balanceSegmentSet.setSegmentIdentifiers(Arrays.asList("am", "fine", "thanks"));
+
+ portfolioManager.createBalanceSegmentSet(product.getIdentifier(), balanceSegmentSet);
+ this.mockMvc.perform(delete("/products/" + product.getIdentifier() + "/balancesegmentsets/" + balanceSegmentSet.getIdentifier())
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .accept(MediaType.ALL_VALUE))
+ .andExpect(status().isAccepted())
+ .andDo(document("document-delete-balance-segment-set", preprocessResponse(prettyPrint())));
+
+ }
+
+
+}
diff --git a/component-test/src/main/java/org/apache/fineract/cn/portfolio/CaseDocumentsApiDocumentation.java b/component-test/src/main/java/org/apache/fineract/cn/portfolio/CaseDocumentsApiDocumentation.java
new file mode 100644
index 0000000..856998a
--- /dev/null
+++ b/component-test/src/main/java/org/apache/fineract/cn/portfolio/CaseDocumentsApiDocumentation.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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.apache.fineract.cn.portfolio;
+
+import com.google.gson.Gson;
+import org.apache.fineract.cn.individuallending.api.v1.domain.caseinstance.CaseCustomerDocuments;
+import org.apache.fineract.cn.portfolio.api.v1.domain.*;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.restdocs.JUnitRestDocumentation;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import java.util.Arrays;
+
+import static org.apache.fineract.cn.lang.config.TenantHeaderFilter.TENANT_HEADER;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.put;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
+import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
+import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
+import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+public class CaseDocumentsApiDocumentation extends AbstractPortfolioTest {
+
+ @Rule
+ public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("build/doc/generated-snippets/test-casedocuments");
+
+ @Autowired
+ private WebApplicationContext context;
+
+ private MockMvc mockMvc;
+
+ @Before
+ public void setUp() {
+
+ this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
+ .apply(documentationConfiguration(this.restDocumentation))
+ .alwaysDo(document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
+ .build();
+ }
+
+ @Test
+ public void documentGetCaseDocuments() throws Exception {
+
+ final Product product = createAndEnableProduct();
+
+ final Case customerCase = createCase(product.getIdentifier());
+
+
+ final CaseCustomerDocuments caseDocuments = caseDocumentsManager.getCaseDocuments(
+ product.getIdentifier(), customerCase.getIdentifier());
+
+ final CaseCustomerDocuments.Document studentLoanDocument
+ = new CaseCustomerDocuments.Document(Fixture.CUSTOMER_IDENTIFIER, "student_loan_documents");
+ final CaseCustomerDocuments.Document houseTitle
+ = new CaseCustomerDocuments.Document(Fixture.CUSTOMER_IDENTIFIER, "house_title");
+ final CaseCustomerDocuments.Document workContract
+ = new CaseCustomerDocuments.Document(Fixture.CUSTOMER_IDENTIFIER, "work_contract");
+
+ caseDocuments.setDocuments(Arrays.asList(studentLoanDocument, houseTitle, workContract));
+
+ this.mockMvc.perform(get("/individuallending/products/" + product.getIdentifier() + "/cases/" + customerCase.getIdentifier() + "/documents")
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .accept(MediaType.ALL_VALUE))
+ .andExpect(status().isOk())
+ .andDo(document("document-get-case-documents", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()),
+ responseFields(
+ fieldWithPath("documents").type("List<Document>").description("The case document +\n" +
+ " +\n" +
+ "_Document_ { +\n" +
+ " *enum* _Type_ { +\n" +
+ " customerId, +\n" +
+ " documentId, +\n" +
+ " } +"))));
+
+ }
+
+
+ @Test
+ public void documentChangeCaseDocuments() throws Exception {
+
+ final Product product = createAndEnableProduct();
+
+ final Case customerCase = createCase(product.getIdentifier());
+
+ final CaseCustomerDocuments caseDocuments = caseDocumentsManager.getCaseDocuments(
+ product.getIdentifier(), customerCase.getIdentifier());
+
+ final CaseCustomerDocuments.Document houseLoanDocument
+ = new CaseCustomerDocuments.Document(Fixture.CUSTOMER_IDENTIFIER, "house_loan_documents");
+ final CaseCustomerDocuments.Document houseTitle
+ = new CaseCustomerDocuments.Document(Fixture.CUSTOMER_IDENTIFIER, "house_title");
+ final CaseCustomerDocuments.Document workContract
+ = new CaseCustomerDocuments.Document(Fixture.CUSTOMER_IDENTIFIER, "work_contract");
+
+ caseDocuments.setDocuments(Arrays.asList(houseLoanDocument, houseTitle, workContract));
+
+ caseDocumentsManager.changeCaseDocuments(product.getIdentifier(), customerCase.getIdentifier(), caseDocuments);
+
+ Gson gson = new Gson();
+ this.mockMvc.perform(put("/individuallending/products/" + product.getIdentifier() + "/cases/" + customerCase.getIdentifier() + "/documents")
+ .accept(MediaType.APPLICATION_JSON_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header(TENANT_HEADER, tenantDataStoreContext.getTenantName())
+ .content(gson.toJson(caseDocuments)))
+ .andExpect(status().isAccepted())
+ .andDo(document(
+ "document-change-case-documents", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()),
+ requestFields(
+ fieldWithPath("documents").type("List<Document>").description("The case document +\n" +
+ " +\n" +
+ "_Document_ { +\n" +
+ " *enum* _Type_ { +\n" +
+ " customerId, +\n" +
+ " documentId, +\n" +
+ " } +"))));
+ }
+
+}
\ No newline at end of file
diff --git a/component-test/src/main/java/org/apache/fineract/cn/portfolio/CasesApiDocumentation.java b/component-test/src/main/java/org/apache/fineract/cn/portfolio/CasesApiDocumentation.java
new file mode 100644
index 0000000..2470942
--- /dev/null
+++ b/component-test/src/main/java/org/apache/fineract/cn/portfolio/CasesApiDocumentation.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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.apache.fineract.cn.portfolio;
+
+import com.google.gson.Gson;
+import org.apache.fineract.cn.individuallending.api.v1.domain.caseinstance.CaseParameters;
+import org.apache.fineract.cn.individuallending.api.v1.domain.product.AccountDesignators;
+import org.apache.fineract.cn.portfolio.api.v1.domain.AccountAssignment;
+import org.apache.fineract.cn.portfolio.api.v1.domain.Case;
+import org.apache.fineract.cn.portfolio.api.v1.domain.Product;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.restdocs.JUnitRestDocumentation;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.apache.fineract.cn.lang.config.TenantHeaderFilter.TENANT_HEADER;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.put;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
+import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
+import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+public class CasesApiDocumentation extends AbstractPortfolioTest {
+
+ @Rule
+ public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("build/doc/generated-snippets/test-cases");
+
+ @Autowired
+ private WebApplicationContext context;
+
+ private MockMvc mockMvc;
+
+ @Before
+ public void setUp() {
+
+ this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
+ .apply(documentationConfiguration(this.restDocumentation))
+ .alwaysDo(document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
+ .build();
+ }
+
+ @Test
+ public void documentCreateCase() throws Exception {
+
+ final Product product = createAndEnableProduct();
+
+ final Case caseInstance = createCase(product.getIdentifier());
+ caseInstance.setIdentifier("case-v1");
+
+ Gson gson = new Gson();
+ this.mockMvc.perform(post("/products/" + product.getIdentifier() + "/cases/")
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .content(gson.toJson(caseInstance)))
+ .andExpect(status().isAccepted())
+ .andDo(document("document-create-case", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()),
+ requestFields(
+ fieldWithPath("identifier").description("Cases's identifier"),
+ fieldWithPath("productIdentifier").description("Products's identifier"),
+ fieldWithPath("interest").description("Cases's interest"),
+ fieldWithPath("parameters").description("cases's parameters"),
+ fieldWithPath("accountAssignments").description("Cases's account assignment"),
+ fieldWithPath("currentState").description("Cases's current state"))));
+
+ }
+
+ @Test
+ public void documentChangeCase() throws Exception {
+
+ final Product product = createAndEnableProduct();
+
+
+ final CaseParameters newCaseParameters = Fixture.createAdjustedCaseParameters(x -> {
+ });
+ final String originalParameters = new Gson().toJson(newCaseParameters);
+ final Case caseInstance = createAdjustedCase(product.getIdentifier(), x -> x.setParameters(originalParameters));
+
+ final Set<AccountAssignment> accountAssignments = new HashSet<>();
+ accountAssignments.add(new AccountAssignment(AccountDesignators.CUSTOMER_LOAN_GROUP, "002-011"));
+ accountAssignments.add(new AccountAssignment(AccountDesignators.ENTRY, "002-012"));
+ caseInstance.setAccountAssignments(accountAssignments);
+
+ Gson gson = new Gson();
+ this.mockMvc.perform(put("/products/" + product.getIdentifier() + "/cases/" + caseInstance.getIdentifier())
+ .accept(MediaType.APPLICATION_JSON_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header(TENANT_HEADER, tenantDataStoreContext.getTenantName())
+ .content(gson.toJson(caseInstance)))
+ .andExpect(status().isAccepted())
+ .andDo(document(
+ "document-change-case", preprocessRequest(prettyPrint()),
+ requestFields(
+ fieldWithPath("identifier").description("Cases's identifier"),
+ fieldWithPath("productIdentifier").description("Products's identifier"),
+ fieldWithPath("interest").description("Cases's interest"),
+ fieldWithPath("parameters").description("cases's parameters"),
+ fieldWithPath("accountAssignments").description("Cases's account assignment"),
+ fieldWithPath("currentState").description("Cases's current state"))));
+
+ }
+}
diff --git a/component-test/src/main/java/org/apache/fineract/cn/portfolio/ChargeDefinitionApiDocumentation.java b/component-test/src/main/java/org/apache/fineract/cn/portfolio/ChargeDefinitionApiDocumentation.java
new file mode 100644
index 0000000..281d25d
--- /dev/null
+++ b/component-test/src/main/java/org/apache/fineract/cn/portfolio/ChargeDefinitionApiDocumentation.java
@@ -0,0 +1,257 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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.apache.fineract.cn.portfolio;
+
+import com.google.gson.Gson;
+import org.apache.fineract.cn.individuallending.api.v1.domain.product.AccountDesignators;
+import org.apache.fineract.cn.individuallending.api.v1.domain.workflow.Action;
+import org.apache.fineract.cn.portfolio.api.v1.domain.ChargeDefinition;
+import org.apache.fineract.cn.portfolio.api.v1.domain.Product;
+import org.apache.fineract.cn.portfolio.api.v1.events.ChargeDefinitionEvent;
+import org.apache.fineract.cn.portfolio.api.v1.events.EventConstants;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.restdocs.JUnitRestDocumentation;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import java.math.BigDecimal;
+
+import static org.apache.fineract.cn.lang.config.TenantHeaderFilter.TENANT_HEADER;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
+import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
+import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
+import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+public class ChargeDefinitionApiDocumentation extends AbstractPortfolioTest {
+
+ @Rule
+ public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("build/doc/generated-snippets/test-chargedefinitions");
+
+ @Autowired
+ private WebApplicationContext context;
+
+ private MockMvc mockMvc;
+
+ @Before
+ public void setUp() {
+
+ this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
+ .apply(documentationConfiguration(this.restDocumentation))
+ .alwaysDo(document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
+ .build();
+ }
+
+ @Test
+ public void documentGetAllChargeDefinitions() throws Exception {
+ final Product product = createProduct();
+
+ try {
+ this.mockMvc.perform(get("/products/" + product.getIdentifier() + "/charges/")
+ .accept(MediaType.APPLICATION_JSON_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header(TENANT_HEADER, tenantDataStoreContext.getTenantName()))
+ .andExpect(status().isOk())
+ .andDo(document(
+ "document-get-all-charge-definitions", preprocessRequest(prettyPrint()),
+ responseFields(
+
+ fieldWithPath("identifier").description("Charge definition's identifier"),
+ fieldWithPath("name").description("Charge definitions given name"),
+ fieldWithPath("description").description("Charge definitions description"),
+ fieldWithPath("accrueAction").description("Charge definitions accrue action"),
+ fieldWithPath("proportionalTo").description("Charge definitions proportional to"),
+ fieldWithPath("accrualAccountDesignator").description("Charge definitions accrual acion generatort"),
+ fieldWithPath("forCycleSizeUnit").description("Charge definitions cycle size unit"),
+ fieldWithPath("forSegmentSet").description("Charge definitions segment set"),
+ fieldWithPath("fromSegment").description("Charge definitions from segment"),
+ fieldWithPath("toSegment").description("Charge definitions to segment"),
+ fieldWithPath("chargeOnTop").description("Charge definitions charge on top"),
+ fieldWithPath("fromAccountDesignator").description("From account designator"),
+ fieldWithPath("toAccountDesignator").description("To account designator"),
+ fieldWithPath("amount").description("Charge definition's amount"),
+ fieldWithPath("chargeMethod").description("Charge definitions charge method"),
+ fieldWithPath("chargeAction").description("Charge definition's charge action"),
+ fieldWithPath("description").description("Employee's middle name"),
+ fieldWithPath("readOnly").description("Readability"))));
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+
+
+ @Test
+ public void documentCreateChargeDefinition() throws Exception {
+
+ final Product product = createProduct();
+
+ final ChargeDefinition chargeDefinition = new ChargeDefinition();
+ chargeDefinition.setIdentifier("charge123");
+ chargeDefinition.setName("core123");
+ chargeDefinition.setFromAccountDesignator("Pembe");
+ chargeDefinition.setToAccountDesignator("Miriam");
+ chargeDefinition.setAmount(BigDecimal.ONE.setScale(3, BigDecimal.ROUND_UNNECESSARY));
+ chargeDefinition.setChargeMethod(ChargeDefinition.ChargeMethod.FIXED);
+ chargeDefinition.setChargeAction(Action.OPEN.name());
+ chargeDefinition.setDescription("describe charge");
+
+
+ Gson gson = new Gson();
+ this.mockMvc.perform(post("/products/" + product.getIdentifier() + "/charges/")
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .content(gson.toJson(chargeDefinition)))
+ .andExpect(status().isAccepted())
+ .andDo(document("document-create-charge-definition", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()),
+ requestFields(
+ fieldWithPath("identifier").description("Charge definition's identifier"),
+ fieldWithPath("name").description("Charge definitions given name"),
+ fieldWithPath("fromAccountDesignator").description("From account designator"),
+ fieldWithPath("toAccountDesignator").description("To account designator"),
+ fieldWithPath("amount").description("Charge definition's amount"),
+ fieldWithPath("chargeMethod").description("Charge definitions charge method"),
+ fieldWithPath("chargeAction").description("Charge definition's charge action"),
+ fieldWithPath("description").description("Employee's middle name"),
+ fieldWithPath("readOnly").description("Readability"))));
+
+ }
+
+ @Test
+ public void documentChangeChargeDefinition() throws Exception {
+
+ final Product product = createProduct();
+
+ final ChargeDefinition chargeDefinition = new ChargeDefinition();
+ chargeDefinition.setIdentifier("charge124");
+ chargeDefinition.setName("core123");
+ chargeDefinition.setFromAccountDesignator("Pembe");
+ chargeDefinition.setToAccountDesignator("Miriam");
+ chargeDefinition.setAmount(BigDecimal.ONE.setScale(3, BigDecimal.ROUND_UNNECESSARY));
+ chargeDefinition.setChargeMethod(ChargeDefinition.ChargeMethod.FIXED);
+ chargeDefinition.setChargeAction(Action.OPEN.name());
+ chargeDefinition.setDescription("describe charge");
+
+ portfolioManager.createChargeDefinition(product.getIdentifier(), chargeDefinition);
+
+ chargeDefinition.setName("charge12345");
+ chargeDefinition.setFromAccountDesignator("Paul");
+ chargeDefinition.setToAccountDesignator("Motia");
+
+ Gson gson = new Gson();
+ this.mockMvc.perform(put("/products/" + product.getIdentifier() + "/charges/" + chargeDefinition.getIdentifier())
+ .accept(MediaType.APPLICATION_JSON_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header(TENANT_HEADER, tenantDataStoreContext.getTenantName())
+ .content(gson.toJson(chargeDefinition)))
+ .andExpect(status().isAccepted())
+ .andDo(document(
+ "document-change-charge-definition", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()),
+ requestFields(
+ fieldWithPath("identifier").description("Charge definition's identifier"),
+ fieldWithPath("name").description("Charge definitions given name"),
+ fieldWithPath("fromAccountDesignator").description("From account designator"),
+ fieldWithPath("toAccountDesignator").description("To account designator"),
+ fieldWithPath("amount").description("Charge definition's amount"),
+ fieldWithPath("chargeMethod").description("Charge definitions charge method"),
+ fieldWithPath("chargeAction").description("Charge definition's charge action"),
+ fieldWithPath("description").description("Employee's middle name"),
+ fieldWithPath("readOnly").description("Readability"))));
+ }
+
+ @Test
+ public void documentGetChargeDefinition() throws Exception {
+
+ final Product product = createProduct();
+
+ final ChargeDefinition chargeDefinition = new ChargeDefinition();
+ chargeDefinition.setIdentifier("charge10");
+ chargeDefinition.setName("core123");
+ chargeDefinition.setFromAccountDesignator("pembe");
+ chargeDefinition.setToAccountDesignator("miriam");
+ chargeDefinition.setAmount(BigDecimal.ONE.setScale(3, BigDecimal.ROUND_UNNECESSARY));
+ chargeDefinition.setChargeMethod(ChargeDefinition.ChargeMethod.FIXED);
+ chargeDefinition.setChargeAction(Action.OPEN.name());
+ chargeDefinition.setDescription("describe charge");
+
+ portfolioManager.createChargeDefinition(product.getIdentifier(), chargeDefinition);
+
+
+ this.mockMvc.perform(get("/products/" + product.getIdentifier() + "/charges/" + chargeDefinition.getIdentifier())
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .accept(MediaType.ALL_VALUE))
+ .andExpect(status().isOk())
+ .andDo(document("document-get-case-document", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()),
+ responseFields(
+ fieldWithPath("identifier").description("Charge definition's identifier"),
+ fieldWithPath("name").description("Charge definitions given name"),
+ fieldWithPath("description").description("Charge definitions description"),
+ fieldWithPath("accrueAction").description("Charge definitions accrue action"),
+ fieldWithPath("proportionalTo").description("Charge definitions proportional to"),
+ fieldWithPath("accrualAccountDesignator").description("Charge definitions accrual acion generatort"),
+ fieldWithPath("forCycleSizeUnit").description("Charge definitions cycle size unit"),
+ fieldWithPath("forSegmentSet").description("Charge definitions segment set"),
+ fieldWithPath("fromSegment").description("Charge definitions from segment"),
+ fieldWithPath("toSegment").description("Charge definitions to segment"),
+ fieldWithPath("chargeOnTop").description("Charge definitions charge on top"),
+ fieldWithPath("fromAccountDesignator").description("From account designator"),
+ fieldWithPath("toAccountDesignator").description("To account designator"),
+ fieldWithPath("amount").description("Charge definition's amount"),
+ fieldWithPath("chargeMethod").description("Charge definitions charge method"),
+ fieldWithPath("chargeAction").description("Charge definition's charge action"),
+ fieldWithPath("description").description("Employee's middle name"),
+ fieldWithPath("readOnly").description("Readability"))));
+ }
+
+ @Test
+ public void documentDeleteChargeDefinition() throws Exception {
+
+ final Product product = createProduct();
+
+ final ChargeDefinition chargeDefinitionToDelete = new ChargeDefinition();
+ chargeDefinitionToDelete.setAmount(BigDecimal.TEN);
+ chargeDefinitionToDelete.setIdentifier("random123");
+ chargeDefinitionToDelete.setName("account");
+ chargeDefinitionToDelete.setDescription("account charge definition");
+ chargeDefinitionToDelete.setChargeAction(Action.APPROVE.name());
+ chargeDefinitionToDelete.setChargeMethod(ChargeDefinition.ChargeMethod.FIXED);
+ chargeDefinitionToDelete.setToAccountDesignator(AccountDesignators.GENERAL_LOSS_ALLOWANCE);
+ chargeDefinitionToDelete.setFromAccountDesignator(AccountDesignators.INTEREST_ACCRUAL);
+ portfolioManager.createChargeDefinition(product.getIdentifier(), chargeDefinitionToDelete);
+ this.eventRecorder.wait(EventConstants.POST_CHARGE_DEFINITION,
+ new ChargeDefinitionEvent(product.getIdentifier(), chargeDefinitionToDelete.getIdentifier()));
+
+ this.mockMvc.perform(delete("/products/" + product.getIdentifier() + "/charges/" + chargeDefinitionToDelete.getIdentifier())
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .accept(MediaType.ALL_VALUE))
+ .andExpect(status().isAccepted())
+ .andDo(document("document-delete-charge-definition", preprocessResponse(prettyPrint())));
+ }
+
+}
diff --git a/component-test/src/main/java/org/apache/fineract/cn/portfolio/LossProvisionApiDocumentation.java b/component-test/src/main/java/org/apache/fineract/cn/portfolio/LossProvisionApiDocumentation.java
new file mode 100644
index 0000000..4673268
--- /dev/null
+++ b/component-test/src/main/java/org/apache/fineract/cn/portfolio/LossProvisionApiDocumentation.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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.apache.fineract.cn.portfolio;
+
+
+import com.google.gson.Gson;
+import org.apache.fineract.cn.individuallending.api.v1.domain.product.LossProvisionConfiguration;
+import org.apache.fineract.cn.individuallending.api.v1.domain.product.LossProvisionStep;
+import org.apache.fineract.cn.portfolio.api.v1.domain.Product;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.restdocs.JUnitRestDocumentation;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.apache.fineract.cn.lang.config.TenantHeaderFilter.TENANT_HEADER;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.put;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
+import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
+import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
+import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+
+public class LossProvisionApiDocumentation extends AbstractPortfolioTest {
+
+ @Rule
+ public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("build/doc/generated-snippets/test-lossprovision");
+
+ @Autowired
+ private WebApplicationContext context;
+
+ private MockMvc mockMvc;
+
+ @Before
+ public void setUp() {
+
+ this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
+ .apply(documentationConfiguration(this.restDocumentation))
+ .alwaysDo(document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
+ .build();
+ }
+
+ @Test
+ public void documentChangeLossProvisionConfiguration() throws Exception {
+ final Product product = createAdjustedProduct(x -> {
+ });
+
+
+ final List<LossProvisionStep> lossProvisionSteps = new ArrayList<>();
+ lossProvisionSteps.add(new LossProvisionStep(1, BigDecimal.valueOf(5_00, 2)));
+ final LossProvisionConfiguration lossProvisionConfiguration = new LossProvisionConfiguration(lossProvisionSteps);
+
+ individualLending.changeLossProvisionConfiguration(product.getIdentifier(), lossProvisionConfiguration);
+
+
+ Gson gson = new Gson();
+ this.mockMvc.perform(put("/individuallending/products/" + product.getIdentifier() + "/lossprovisionconfiguration")
+ .accept(MediaType.APPLICATION_JSON_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header(TENANT_HEADER, tenantDataStoreContext.getTenantName())
+ .content(gson.toJson(lossProvisionConfiguration)))
+ .andExpect(status().isAccepted())
+ .andDo(document(
+ "document-change-loss-provision-configuration", preprocessRequest(prettyPrint()),
+ requestFields(
+ fieldWithPath("lossProvisionSteps").type("List<LossProvisionSteps>").description("The loss provision configurations +\n" +
+ " +\n" +
+ "_LossProvisionSteps_ { +\n" +
+ " *enum* _Type_ { +\n" +
+ " daysLate, +\n" +
+ " percentProvision, +\n" +
+ " } +")
+ )
+ ));
+ }
+
+ @Test
+ public void documentGetLossProvisionConfiguration() throws Exception {
+
+ final Product product = createAdjustedProduct(x -> {
+ });
+
+ final List<LossProvisionStep> lossProvisionSteps1 = new ArrayList<>();
+ lossProvisionSteps1.add(new LossProvisionStep(0, BigDecimal.valueOf(1_00, 2)));
+ lossProvisionSteps1.add(new LossProvisionStep(1, BigDecimal.valueOf(9_00, 2)));
+ lossProvisionSteps1.add(new LossProvisionStep(30, BigDecimal.valueOf(35_00, 2)));
+ lossProvisionSteps1.add(new LossProvisionStep(60, BigDecimal.valueOf(55_00, 2)));
+ final LossProvisionConfiguration lossProvisionConfiguration = new LossProvisionConfiguration(lossProvisionSteps1);
+
+ individualLending.changeLossProvisionConfiguration(product.getIdentifier(), lossProvisionConfiguration);
+
+ try {
+ this.mockMvc.perform(get("/individuallending/products/" + product.getIdentifier() + "/lossprovisionconfiguration")
+ .accept(MediaType.APPLICATION_JSON_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header(TENANT_HEADER, tenantDataStoreContext.getTenantName()))
+ .andExpect(status().isOk())
+ .andDo(document(
+ "document-get-loss-provision-configuration", preprocessRequest(prettyPrint()),
+ responseFields(
+ fieldWithPath("lossProvisionSteps").type("List<LossProvisionSteps>").description("The loss provision configurations +\n" +
+ " +\n" +
+ "_LossProvisionSteps_ { +\n" +
+ " *enum* _Type_ { +\n" +
+ " daysLate, +\n" +
+ " percentProvision, +\n" +
+ " } +")
+ )
+ ));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/component-test/src/main/java/org/apache/fineract/cn/portfolio/PatternApiDocumentation.java b/component-test/src/main/java/org/apache/fineract/cn/portfolio/PatternApiDocumentation.java
new file mode 100644
index 0000000..2d9c6a9
--- /dev/null
+++ b/component-test/src/main/java/org/apache/fineract/cn/portfolio/PatternApiDocumentation.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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.apache.fineract.cn.portfolio;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.restdocs.JUnitRestDocumentation;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import static org.apache.fineract.cn.lang.config.TenantHeaderFilter.TENANT_HEADER;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
+import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
+import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+public class PatternApiDocumentation extends AbstractPortfolioTest {
+ @Rule
+ public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("build/doc/generated-snippets/test-pattern");
+
+ @Autowired
+ private WebApplicationContext context;
+
+ private MockMvc mockMvc;
+
+ @Before
+ public void setUp() {
+
+ this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
+ .apply(documentationConfiguration(this.restDocumentation))
+ .alwaysDo(document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
+ .build();
+ }
+
+ @Test
+ public void documentGetPatterns() throws Exception {
+
+ try {
+ this.mockMvc.perform(get("/patterns/")
+ .accept(MediaType.APPLICATION_JSON_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header(TENANT_HEADER, tenantDataStoreContext.getTenantName()))
+ .andExpect(status().isOk())
+ .andDo(document(
+ "document-get-patterns", preprocessRequest(prettyPrint()),
+ responseFields(
+ fieldWithPath("[].parameterPackage").description("Pattern's parameter package"),
+ fieldWithPath("[].accountAssignmentGroups").description("Pattern's groups"),
+ fieldWithPath("[].accountAssignmentsRequired").description("List of Pattern's account assignments")
+ )
+
+ ));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/component-test/src/main/java/org/apache/fineract/cn/portfolio/PlannedPaymentsApiDocumentation.java b/component-test/src/main/java/org/apache/fineract/cn/portfolio/PlannedPaymentsApiDocumentation.java
new file mode 100644
index 0000000..dee2c3a
--- /dev/null
+++ b/component-test/src/main/java/org/apache/fineract/cn/portfolio/PlannedPaymentsApiDocumentation.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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.apache.fineract.cn.portfolio;
+
+import com.google.gson.Gson;
+import org.apache.fineract.cn.portfolio.api.v1.domain.Case;
+import org.apache.fineract.cn.portfolio.api.v1.domain.Product;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.restdocs.JUnitRestDocumentation;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import static org.apache.fineract.cn.lang.config.TenantHeaderFilter.TENANT_HEADER;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
+import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
+import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
+import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+public class PlannedPaymentsApiDocumentation extends AbstractPortfolioTest {
+
+ @Rule
+ public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("build/doc/generated-snippets/test-plannedpayments");
+
+ @Autowired
+ private WebApplicationContext context;
+
+ private MockMvc mockMvc;
+
+ @Before
+ public void setUp() {
+
+ this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
+ .apply(documentationConfiguration(this.restDocumentation))
+ .alwaysDo(document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
+ .build();
+ }
+
+ @Test
+ public void documentGetPaymentScheduledForCase() throws Exception {
+ final Product product = createAndEnableProduct();
+ final Case caseInstance = createCase(product.getIdentifier());
+
+ this.mockMvc.perform(get("/individuallending/products/" + product.getIdentifier() + "/cases/" + caseInstance.getIdentifier() + "/plannedpayments")
+ .accept(MediaType.APPLICATION_JSON_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header(TENANT_HEADER, tenantDataStoreContext.getTenantName()))
+ .andExpect(status().isOk())
+ .andDo(document(
+ "document-get-payment-scheduled-for-case", preprocessRequest(prettyPrint()),
+ responseFields(
+ fieldWithPath("chargeNames").description("Charge names"),
+ fieldWithPath("elements").description("Payments"),
+ fieldWithPath("totalPages").description("Total number of pages "),
+ fieldWithPath("totalElements").description("Total elements found"))));
+
+ }
+
+ @Test
+ public void documentGetPaymentScheduledForParameters() throws Exception {
+ final Product product = createAndEnableProduct();
+ final Case caseInstance = createCase(product.getIdentifier());
+
+ Gson gson = new Gson();
+ this.mockMvc.perform(post("/individuallending/products/" + product.getIdentifier() + "/plannedpayments")
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .content(gson.toJson(caseInstance)))
+ .andExpect(status().isOk())
+ .andDo(document("document-get-payment-scheduled-for-parameters", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()),
+ requestFields(
+ fieldWithPath("identifier").description("Cases's identifier"),
+ fieldWithPath("productIdentifier").description("Products's identifier"),
+ fieldWithPath("interest").description("Cases's interest"),
+ fieldWithPath("parameters").description("cases's parameters"),
+ fieldWithPath("accountAssignments").description("Cases's account assignment"),
+ fieldWithPath("currentState").description("Cases's current state"))));
+
+ }
+
+}
diff --git a/component-test/src/main/java/org/apache/fineract/cn/portfolio/ProductsApiDocumentation.java b/component-test/src/main/java/org/apache/fineract/cn/portfolio/ProductsApiDocumentation.java
new file mode 100644
index 0000000..e37237e
--- /dev/null
+++ b/component-test/src/main/java/org/apache/fineract/cn/portfolio/ProductsApiDocumentation.java
@@ -0,0 +1,255 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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.apache.fineract.cn.portfolio;
+
+import com.google.gson.Gson;
+import org.apache.fineract.cn.portfolio.api.v1.domain.Product;
+import org.apache.fineract.cn.portfolio.api.v1.events.EventConstants;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.restdocs.JUnitRestDocumentation;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import static org.apache.fineract.cn.lang.config.TenantHeaderFilter.TENANT_HEADER;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
+import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
+import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
+import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+public class ProductsApiDocumentation extends AbstractPortfolioTest {
+
+ @Rule
+ public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("build/doc/generated-snippets/test-product");
+
+ @Autowired
+ private WebApplicationContext context;
+
+ private MockMvc mockMvc;
+
+ @Before
+ public void setUp() {
+
+ this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
+ .apply(documentationConfiguration(this.restDocumentation))
+ .alwaysDo(document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
+ .build();
+ }
+
+ @Test
+ public void documentCreateProduct() throws Exception {
+ final Product product = createAdjustedProduct(x -> {
+ });
+ product.setIdentifier("agro11");
+
+ Gson gson = new Gson();
+ this.mockMvc.perform(post("/products/")
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .content(gson.toJson(product)))
+ .andExpect(status().isAccepted())
+ .andDo(document("document-create-product", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()),
+ requestFields(
+ fieldWithPath("identifier").description("Product's identifier"),
+ fieldWithPath("name").description("Product's given name"),
+ fieldWithPath("termRange").type("List<TermRange>").description("The term range +\n" +
+ " +\n" +
+ "_TermRange_ { +\n" +
+ " *enum* _Type_ { +\n" +
+ " temporalUnit, +\n" +
+ " maximum, +\n" +
+ " } +"),
+ fieldWithPath("balanceRange").description("Product's balance range"),
+ fieldWithPath("interestRange").description("Products interest Range"),
+ fieldWithPath("interestBasis").description("Products's interest basis"),
+ fieldWithPath("patternPackage").description("Product's pattern package"),
+ fieldWithPath("description").description("product description"),
+ fieldWithPath("currencyCode").description("Country currency code"),
+ fieldWithPath("minorCurrencyUnitDigits").description("Country minor currency unit"),
+ fieldWithPath("accountAssignments").description("Account Assignments"),
+ fieldWithPath("parameters").description("Product's parameters"),
+ fieldWithPath("enabled").description("Readability"))));
+
+ }
+
+
+ @Test
+ public void documentGetProducts() throws Exception {
+ final Product product = createAdjustedProduct(x -> {
+ });
+
+ this.eventRecorder.wait(EventConstants.POST_PRODUCT, product.getIdentifier());
+
+ try {
+ this.mockMvc.perform(get("/products?pageIndex=0&size=200")
+ .accept(MediaType.APPLICATION_JSON_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header(TENANT_HEADER, tenantDataStoreContext.getTenantName()))
+ .andExpect(status().isOk())
+ .andDo(document(
+ "document-get-products", preprocessRequest(prettyPrint()),
+ responseFields(
+ fieldWithPath("identifier").description("Product's identifier"),
+ fieldWithPath("name").description("Product's given name"),
+ fieldWithPath("termRange").type("List<TermRange>").description("The term range +\n" +
+ " +\n" +
+ "_TermRange_ { +\n" +
+ " *enum* _Type_ { +\n" +
+ " temporalUnit, +\n" +
+ " maximum, +\n" +
+ " } +"),
+ fieldWithPath("balanceRange").description("Product's balance range"),
+ fieldWithPath("interestRange").description("Products interest Range"),
+ fieldWithPath("interestBasis").description("Products's interest basis"),
+ fieldWithPath("patternPackage").description("Product's pattern package"),
+ fieldWithPath("description").description("product description"),
+ fieldWithPath("currencyCode").description("Country currency code"),
+ fieldWithPath("minorCurrencyUnitDigits").description("Country minor currency unit"),
+ fieldWithPath("accountAssignments").description("Account Assignments"),
+ fieldWithPath("parameters").description("Product's parameters"),
+ fieldWithPath("enabled").description("Readability"))));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ @Test
+ public void documentDeleteProducts() throws Exception {
+
+ final Product product = createAdjustedProduct(x -> {
+ });
+
+
+ this.mockMvc.perform(delete("/products/" + product.getIdentifier())
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .accept(MediaType.ALL_VALUE))
+ .andExpect(status().isAccepted())
+ .andDo(document("document-delete-products", preprocessResponse(prettyPrint())));
+
+ }
+
+ @Test
+ public void documentGetProduct() throws Exception {
+
+ final Product product = createAdjustedProduct(x -> {
+ });
+
+ this.mockMvc.perform(get("/products/" + product.getIdentifier())
+ .accept(MediaType.APPLICATION_JSON_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header(TENANT_HEADER, tenantDataStoreContext.getTenantName()))
+ .andExpect(status().isOk())
+ .andDo(document(
+ "document-get-product", preprocessRequest(prettyPrint()),
+ responseFields(
+ fieldWithPath("identifier").description("Charge definition's identifier"),
+ fieldWithPath("name").description("Charge definitions given name"),
+ fieldWithPath("termRange").type("List<TermRange>").description("The term range +\n" +
+ " +\n" +
+ "_TermRange_ { +\n" +
+ " *enum* _Type_ { +\n" +
+ " temporalUnit, +\n" +
+ " maximum, +\n" +
+ " } +"),
+ fieldWithPath("balanceRange").description("From account designator"),
+ fieldWithPath("interestRange").description("To account designator"),
+ fieldWithPath("interestBasis").description("Charge definition's amount"),
+ fieldWithPath("patternPackage").description("Charge definitions charge method"),
+ fieldWithPath("description").description("Charge definitions charge method"),
+ fieldWithPath("currencyCode").description("Charge definition's charge action"),
+ fieldWithPath("minorCurrencyUnitDigits").description("Employee's middle name"),
+ fieldWithPath("accountAssignments").description("Readability"),
+ fieldWithPath("parameters").description("Readability"),
+ fieldWithPath("enabled").description("Readability"),
+ fieldWithPath("createdOn").description("Readability"),
+ fieldWithPath("createdBy").description("Readability"),
+ fieldWithPath("lastModifiedOn").description("Readability"),
+ fieldWithPath("lastModifiedBy").description("Readability"))));
+
+ }
+
+ @Test
+ public void documentChangeProduct() throws Exception {
+
+ final Product product = createAdjustedProduct(x -> {
+ });
+ product.setName("akawo");
+
+ Gson gson = new Gson();
+ this.mockMvc.perform(put("/products/" + product.getIdentifier())
+ .accept(MediaType.APPLICATION_JSON_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header(TENANT_HEADER, tenantDataStoreContext.getTenantName())
+ .content(gson.toJson(product)))
+ .andExpect(status().isAccepted())
+ .andDo(document(
+ "document-change-product", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()),
+ requestFields(
+ fieldWithPath("identifier").description("Charge definition's identifier"),
+ fieldWithPath("name").description("Charge definitions given name"),
+ fieldWithPath("termRange").type("List<TermRange>").description("The term range +\n" +
+ " +\n" +
+ "_TermRange_ { +\n" +
+ " *enum* _Type_ { +\n" +
+ " temporalUnit, +\n" +
+ " maximum, +\n" +
+ " } +"),
+ fieldWithPath("balanceRange").description("From account designator"),
+ fieldWithPath("interestRange").description("To account designator"),
+ fieldWithPath("interestBasis").description("Charge definition's amount"),
+ fieldWithPath("patternPackage").description("Charge definitions charge method"),
+ fieldWithPath("description").description("Charge definitions charge method"),
+ fieldWithPath("currencyCode").description("Charge definition's charge action"),
+ fieldWithPath("minorCurrencyUnitDigits").description("Employee's middle name"),
+ fieldWithPath("accountAssignments").description("Readability"),
+ fieldWithPath("parameters").description("Readability"),
+ fieldWithPath("enabled").description("Readability"))));
+
+ }
+
+ @Test
+ public void documentGetIncompleteAccountAssignments() throws Exception {
+
+ final Product product = createAdjustedProduct(x -> {
+ });
+
+ this.mockMvc.perform(get("/products/" + product.getIdentifier() + "/incompleteaccountassignments")
+ .accept(MediaType.APPLICATION_JSON_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header(TENANT_HEADER, tenantDataStoreContext.getTenantName()))
+ .andExpect(status().isOk())
+ .andDo(document(
+ "document-get-incomplete-account-assignments", preprocessRequest(prettyPrint()),
+ responseFields(
+ )));
+
+ }
+
+
+}
diff --git a/component-test/src/main/java/org/apache/fineract/cn/portfolio/TaskDefinitionApiDocumentation.java b/component-test/src/main/java/org/apache/fineract/cn/portfolio/TaskDefinitionApiDocumentation.java
new file mode 100644
index 0000000..a3d62ed
--- /dev/null
+++ b/component-test/src/main/java/org/apache/fineract/cn/portfolio/TaskDefinitionApiDocumentation.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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.apache.fineract.cn.portfolio;
+
+import com.google.gson.Gson;
+import org.apache.fineract.cn.portfolio.api.v1.domain.Product;
+import org.apache.fineract.cn.portfolio.api.v1.domain.TaskDefinition;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.restdocs.JUnitRestDocumentation;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import static org.apache.fineract.cn.lang.config.TenantHeaderFilter.TENANT_HEADER;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
+import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
+import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
+import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+public class TaskDefinitionApiDocumentation extends AbstractPortfolioTest {
+
+ @Rule
+ public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("build/doc/generated-snippets/test-taskdefinition");
+
+ @Autowired
+ private WebApplicationContext context;
+
+ private MockMvc mockMvc;
+
+ @Before
+ public void setUp() {
+
+ this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
+ .apply(documentationConfiguration(this.restDocumentation))
+ .alwaysDo(document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
+ .build();
+ }
+
+ @Test
+ public void documentListTaskDefinitions() throws Exception {
+ final Product product = createProduct();
+
+ try {
+ this.mockMvc.perform(get("/products/" + product.getIdentifier() + "/tasks/")
+ .accept(MediaType.APPLICATION_JSON_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header(TENANT_HEADER, tenantDataStoreContext.getTenantName()))
+ .andExpect(status().isOk())
+ .andDo(document(
+ "document-list-task-definitions", preprocessRequest(prettyPrint()),
+ responseFields(
+ fieldWithPath("identifier").type("String").description("task identifier's identifier"),
+ fieldWithPath("name").type("String").description("task identifier's name"),
+ fieldWithPath("description").type("String").description("task identifier's description"),
+ fieldWithPath("actions").description("The task definition action"),
+ fieldWithPath("fourEyes").type("String").description("task identifier's identifier"),
+ fieldWithPath("mandatory").type("String").description("task identifier's identifier")
+ )
+ ));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+
+
+ @Test
+ public void documentGetTaskDefinition() throws Exception {
+ final Product product = createProduct();
+ final TaskDefinition taskDefinition = createTaskDefinition(product);
+
+ try {
+ this.mockMvc.perform(get("/products/" + product.getIdentifier() + "/tasks/" + taskDefinition.getIdentifier())
+ .accept(MediaType.APPLICATION_JSON_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header(TENANT_HEADER, tenantDataStoreContext.getTenantName()))
+ .andExpect(status().isOk())
+ .andDo(document(
+ "document-get-task-definition", preprocessRequest(prettyPrint()),
+ responseFields(
+ fieldWithPath("identifier").type("String").description("task identifier's identifier"),
+ fieldWithPath("name").type("String").description("task identifier's name"),
+ fieldWithPath("description").type("String").description("task identifier's description"),
+ fieldWithPath("actions").description("The task definition action"),
+ fieldWithPath("fourEyes").type("String").description("task identifier's identifier"),
+ fieldWithPath("mandatory").type("String").description("task identifier's identifier")
+ )
+ ));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ @Test
+ public void documentChangeTaskDefinition() throws Exception {
+
+ final Product product = createProduct();
+ final TaskDefinition taskDefinition = createTaskDefinition(product);
+ taskDefinition.setDescription("Describe task definition");
+ taskDefinition.setFourEyes(false);
+
+ Gson gson = new Gson();
+ this.mockMvc.perform(put("/products/" + product.getIdentifier() + "/tasks/" + taskDefinition.getIdentifier())
+ .accept(MediaType.APPLICATION_JSON_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header(TENANT_HEADER, tenantDataStoreContext.getTenantName())
+ .content(gson.toJson(taskDefinition)))
+ .andExpect(status().isAccepted())
+ .andDo(document(
+ "document-change-task-definition", preprocessRequest(prettyPrint()),
+ requestFields(
+ fieldWithPath("identifier").type("String").description("task identifier's identifier"),
+ fieldWithPath("name").type("String").description("task identifier's name"),
+ fieldWithPath("description").type("String").description("task identifier's description"),
+ fieldWithPath("actions").description("The task definition action"),
+ fieldWithPath("fourEyes").type("String").description("task identifier's identifier"),
+ fieldWithPath("mandatory").type("String").description("task identifier's identifier")
+ )
+
+ ));
+
+ }
+
+ @Test
+ public void documentAddTaskDefinition() throws Exception {
+
+ final Product product = createProduct();
+ final TaskDefinition taskDefinition = createTaskDefinition(product);
+ taskDefinition.setIdentifier("ask");
+
+ Gson gson = new Gson();
+ this.mockMvc.perform(post("/products/" + product.getIdentifier() + "/tasks/")
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .content(gson.toJson(taskDefinition)))
+ .andExpect(status().isAccepted())
+ .andDo(document("document-add-task-definition", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()),
+ requestFields(
+ fieldWithPath("identifier").type("String").description("task identifier's identifier"),
+ fieldWithPath("name").type("String").description("task identifier's name"),
+ fieldWithPath("description").type("String").description("task identifier's description"),
+ fieldWithPath("actions").description("The task definition action"),
+ fieldWithPath("fourEyes").type("String").description("task identifier's identifier"),
+ fieldWithPath("mandatory").type("String").description("task identifier's identifier"))));
+ }
+
+ @Test
+ public void documentDeleteTaskDefinition() throws Exception {
+ final Product product = createProduct();
+ final TaskDefinition taskDefinition = createTaskDefinition(product);
+
+ this.mockMvc.perform(delete("/products/" + product.getIdentifier() + "/tasks/" + taskDefinition.getIdentifier())
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .accept(MediaType.ALL_VALUE))
+ .andExpect(status().isAccepted())
+ .andDo(document("document-delete-task-definition", preprocessResponse(prettyPrint())));
+ }
+}
diff --git a/service/build.gradle b/service/build.gradle
index 2d55f52..de6e948 100644
--- a/service/build.gradle
+++ b/service/build.gradle
@@ -32,6 +32,7 @@
plugins {
id 'com.github.hierynomus.license' version '0.13.1'
id("org.nosphere.apache.rat") version "0.3.1"
+ id "com.jfrog.artifactory" version "4.9.5"
}
apply from: '../shared.gradle'
@@ -76,14 +77,14 @@
from components.java
groupId project.group
artifactId project.name
- version project.version
+ version project.findProperty('externalVersion') ?: project.version
}
bootService(MavenPublication) {
// "boot" jar
artifact ("$buildDir/libs/$project.name-$version-boot.jar")
groupId project.group
artifactId ("$project.name-boot")
- version project.version
+ version project.findProperty('externalVersion') ?: project.version
}
}
}
diff --git a/shared.gradle b/shared.gradle
index ceb0f70..68dde5b 100644
--- a/shared.gradle
+++ b/shared.gradle
@@ -48,6 +48,7 @@
repositories {
jcenter()
mavenLocal()
+ maven { url 'https://mifos.jfrog.io/mifos/libs-snapshot/' }
}
dependencyManagement {
@@ -79,6 +80,22 @@
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
+artifactory {
+ contextUrl = System.getenv("ARTIFACTORY_URL")
+ publish {
+ repository {
+ repoKey = project.findProperty('artifactoryRepoKey')
+ username = System.getenv("ARTIFACTORY_USER")
+ password = System.getenv("ARTIFACTORY_PASSWORD")
+ }
+
+ defaults {
+ publications ('api', 'componentTest', 'service', 'bootService')
+ }
+ }
+}
+artifactoryPublish.dependsOn('clean','publishToMavenLocal')
+
license {
header rootProject.file('../HEADER')
strictCheck true
@@ -94,6 +111,7 @@
rat {
// List of exclude directives, defaults to ['**/.gradle/**']
excludes = [
+ "**/.dockerignore/**",
"**/.idea/**",
"**/.gradle/**",
"**/gradle/**",
@@ -102,4 +120,5 @@
"gradlew.bat",
"README.md"
]
+ plainOutput = true
}
diff --git a/travis.sh b/travis.sh
new file mode 100755
index 0000000..03a75ee
--- /dev/null
+++ b/travis.sh
@@ -0,0 +1,71 @@
+#!/usr/bin/env bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF 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.
+#
+
+# Documentation: https://cwiki.apache.org/confluence/display/FINERACT/Fineract-CN+Artifactory
+
+#Exit immediately if a command exits with a non-zero status.
+set -e
+EXIT_STATUS=0
+
+# Builds and Publishes a SNAPSHOT
+function build_snapshot() {
+ echo -e "Building and publishing a snapshot out of branch [$TRAVIS_BRANCH]"
+ ./gradlew -PartifactoryRepoKey=libs-snapshot-local -DbuildInfo.build.number=${TRAVIS_COMMIT::7} artifactoryPublish --stacktrace || EXIT_STATUS=$?
+}
+
+# Builds a Pull Request
+function build_pullrequest() {
+ echo -e "Building pull request #$TRAVIS_PULL_REQUEST of branch [$TRAVIS_BRANCH]. Won't publish anything to Artifactory."
+ ./gradlew publishToMavenLocal rat || EXIT_STATUS=$?
+}
+
+# For other branches we need to add branch name as prefix
+function build_otherbranch() {
+ echo -e "Building a snapshot out of branch [$TRAVIS_BRANCH] and publishing it with prefix '${TRAVIS_BRANCH}-SNAPSHOT'"
+ ./gradlew -PartifactoryRepoKey=libs-snapshot-local -DbuildInfo.build.number=${TRAVIS_COMMIT::7} -PexternalVersion=${TRAVIS_BRANCH}-SNAPSHOT artifactoryPublish --stacktrace || EXIT_STATUS=$?
+}
+
+# Builds and Publishes a Tag
+function build_tag() {
+ echo -e "Building tag [$TRAVIS_TAG] and publishing it as a release"
+ ./gradlew -PartifactoryRepoKey=libs-release-local -PexternalVersion=$TRAVIS_TAG artifactoryPublish --stacktrace || EXIT_STATUS=$?
+
+}
+
+echo -e "TRAVIS_BRANCH=$TRAVIS_BRANCH"
+echo -e "TRAVIS_TAG=$TRAVIS_TAG"
+echo -e "TRAVIS_COMMIT=${TRAVIS_COMMIT::7}"
+echo -e "TRAVIS_PULL_REQUEST=$TRAVIS_PULL_REQUEST"
+
+# Build Logic
+if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
+ build_pullrequest
+elif [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_BRANCH" != "$BUILD_SNAPSHOTS_BRANCH" ] && [ "$TRAVIS_TAG" == "" ] ; then
+ build_otherbranch
+elif [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_BRANCH" == "$BUILD_SNAPSHOTS_BRANCH" ] && [ "$TRAVIS_TAG" == "" ] ; then
+ build_snapshot
+elif [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_TAG" != "" ]; then
+ build_tag
+else
+ echo -e "WARN: Unexpected env variable values => Branch [$TRAVIS_BRANCH], Tag [$TRAVIS_TAG], Pull Request [#$TRAVIS_PULL_REQUEST]"
+ ./gradlew clean build
+fi
+
+exit ${EXIT_STATUS}