You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by aw...@apache.org on 2019/06/03 19:03:17 UTC
[fineract-cn-payroll] 42/50: Document Payroll API to configure
payroll alocations
This is an automated email from the ASF dual-hosted git repository.
awasum pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract-cn-payroll.git
commit 6094e1ecbdc916da558d1fca4ec655f7127d1b9b
Author: Isaac Kamga <u2...@gmail.com>
AuthorDate: Tue Sep 4 13:16:29 2018 +0100
Document Payroll API to configure payroll alocations
---
component-test/build.gradle | 13 +-
.../cn/payroll/PayrollApiDocumentation.java | 395 +++++++++++++++++++++
2 files changed, 407 insertions(+), 1 deletion(-)
diff --git a/component-test/build.gradle b/component-test/build.gradle
index 95878f3..7723165 100644
--- a/component-test/build.gradle
+++ b/component-test/build.gradle
@@ -26,6 +26,7 @@ buildscript {
dependencies {
classpath ("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
+ classpath("org.asciidoctor:asciidoctor-gradle-plugin:1.5.3")
}
}
@@ -34,6 +35,7 @@ plugins {
id("org.nosphere.apache.rat") version "0.3.1"
}
apply from: '../shared.gradle'
+apply plugin: 'org.asciidoctor.convert'
dependencies {
compile(
@@ -43,10 +45,19 @@ dependencies {
[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) {
diff --git a/component-test/src/main/java/org/apache/fineract/cn/payroll/PayrollApiDocumentation.java b/component-test/src/main/java/org/apache/fineract/cn/payroll/PayrollApiDocumentation.java
new file mode 100644
index 0000000..39a38fc
--- /dev/null
+++ b/component-test/src/main/java/org/apache/fineract/cn/payroll/PayrollApiDocumentation.java
@@ -0,0 +1,395 @@
+package org.apache.fineract.cn.payroll;
+
+import com.google.common.collect.Lists;
+import com.google.gson.Gson;
+import org.apache.fineract.cn.accounting.api.v1.domain.Account;
+import org.apache.fineract.cn.customer.api.v1.domain.Customer;
+
+import org.apache.fineract.cn.payroll.api.v1.EventConstants;
+import org.apache.fineract.cn.payroll.api.v1.domain.*;
+import org.apache.fineract.cn.payroll.domain.DomainObjectGenerator;
+import org.apache.fineract.cn.payroll.service.internal.repository.PayrollCollectionEntity;
+import org.apache.fineract.cn.payroll.service.internal.service.adaptor.AccountingAdaptor;
+import org.apache.fineract.cn.payroll.service.internal.service.adaptor.CustomerAdaptor;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.mockito.Mockito;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.mock.mockito.MockBean;
+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 java.util.Optional;
+
+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.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.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.put;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
+import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
+import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
+import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
+
+public class PayrollApiDocumentation extends AbstractPayrollTest {
+
+ @Rule
+ public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("build/doc/generated-snippets/test-payroll");
+
+ @Autowired
+ private WebApplicationContext context;
+
+ private MockMvc mockMvc;
+
+ @MockBean
+ private CustomerAdaptor customerAdaptor;
+
+ @MockBean
+ private AccountingAdaptor accountingAdaptor;
+
+ @Before
+ public void setUp ( ) {
+
+ this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
+ .apply(documentationConfiguration(this.restDocumentation))
+ .build();
+ }
+
+ @Test
+ public void documentSetPayrollConfiguration ( ) throws Exception {
+
+ final PayrollConfiguration payrollConfiguration = DomainObjectGenerator.getPayrollConfiguration();
+ payrollConfiguration.setMainAccountNumber("12345678910");
+
+ final PayrollAllocation savingsAllocation = new PayrollAllocation();
+ savingsAllocation.setAccountNumber("9876543210");
+ savingsAllocation.setAmount(BigDecimal.valueOf(5500.00D));
+ savingsAllocation.setProportional(Boolean.TRUE);
+
+ final PayrollAllocation tradeUnionAllocation = new PayrollAllocation();
+ tradeUnionAllocation.setAccountNumber("24681097531");
+ tradeUnionAllocation.setAmount(BigDecimal.valueOf(43.00D));
+ tradeUnionAllocation.setProportional(Boolean.TRUE);
+
+ final ArrayList <PayrollAllocation> payrollAllocations = new ArrayList <>();
+ payrollConfiguration.setPayrollAllocations(payrollAllocations);
+ payrollAllocations.add(savingsAllocation);
+ payrollAllocations.add(tradeUnionAllocation);
+
+ final Customer customer = new Customer();
+ customer.setIdentifier("customerOne");
+
+ this.prepareConfigurationMocks(customer.getIdentifier(), payrollConfiguration);
+
+ Gson gson = new Gson();
+ this.mockMvc.perform(put("/customers/" + customer.getIdentifier() + "/payroll")
+ .accept(MediaType.APPLICATION_JSON_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .content(gson.toJson(payrollConfiguration)))
+ .andExpect(status().isAccepted())
+ .andDo(document("document-set-payroll-configuration", preprocessRequest(prettyPrint()),
+ requestFields(
+ fieldWithPath("mainAccountNumber").description("Main account number"),
+ fieldWithPath("payrollAllocations[].accountNumber").description("Account from where you pay first allocation"),
+ fieldWithPath("payrollAllocations[].amount").type("Integer").description("Amount to be paid during first allocation"),
+ fieldWithPath("payrollAllocations[].proportional").type("Boolean").description("Should payments be proportional ?"),
+ fieldWithPath("payrollAllocations[1].accountNumber").description("Account from where you pay second allocation"),
+ fieldWithPath("payrollAllocations[1].amount").type("Integer").description("Amount to be paid during first allocation"),
+ fieldWithPath("payrollAllocations[1].proportional").type("Boolean").description("Should payments be proportional ?")
+ )
+ ));
+ }
+
+ @Test
+ public void documentFindPayrollConfiguration ( ) throws Exception {
+
+ final PayrollConfiguration payrollConf = DomainObjectGenerator.getPayrollConfiguration();
+ payrollConf.setMainAccountNumber("AB12345");
+
+ final PayrollAllocation savingsAllocation = new PayrollAllocation();
+ savingsAllocation.setAccountNumber("BAH97531");
+ savingsAllocation.setAmount(BigDecimal.valueOf(73.00D));
+ savingsAllocation.setProportional(Boolean.TRUE);
+
+ final PayrollAllocation tradeUnionAllocation = new PayrollAllocation();
+ tradeUnionAllocation.setAccountNumber("CAG24680");
+ tradeUnionAllocation.setAmount(BigDecimal.valueOf(21.00D));
+ tradeUnionAllocation.setProportional(Boolean.TRUE);
+
+ final ArrayList <PayrollAllocation> payrollAllocations = new ArrayList <>();
+ payrollConf.setPayrollAllocations(payrollAllocations);
+ payrollAllocations.add(savingsAllocation);
+ payrollAllocations.add(tradeUnionAllocation);
+
+ final Customer customer = new Customer();
+ customer.setIdentifier("faundCostoma");
+
+ this.prepareConfigurationMocks(customer.getIdentifier(), payrollConf);
+ super.testSubject.setPayrollConfiguration(customer.getIdentifier(), payrollConf);
+ super.eventRecorder.wait(EventConstants.PUT_CONFIGURATION, customer.getIdentifier());
+
+ this.mockMvc.perform(get("/customers/" + customer.getIdentifier() + "/payroll")
+ .accept(MediaType.APPLICATION_JSON_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE))
+ .andExpect(status().isOk())
+ .andDo(document("document-find-payroll-configuration", preprocessResponse(prettyPrint()),
+ responseFields(
+ fieldWithPath("mainAccountNumber").description("Main account number"),
+ fieldWithPath("payrollAllocations[].accountNumber").description("Account from where you pay first allocation"),
+ fieldWithPath("payrollAllocations[].amount").type("Integer").description("Amount to be paid during first allocation"),
+ fieldWithPath("payrollAllocations[].proportional").type("Boolean").description("Should payments be proportional ?"),
+ fieldWithPath("payrollAllocations[1].accountNumber").description("Account from where you pay second allocation"),
+ fieldWithPath("payrollAllocations[1].amount").type("Integer").description("Amount to be paid during first allocation"),
+ fieldWithPath("payrollAllocations[1].proportional").type("Boolean").description("Should payments be proportional ?"),
+ fieldWithPath("createdBy").description("Employee who configured payroll"),
+ fieldWithPath("createdOn").description("Date when payroll was configured"),
+ fieldWithPath("lastModifiedBy").type("String").description("Employee who last modified payroll"),
+ fieldWithPath("lastModifiedOn").type("String").description("Date when payroll was last modified")
+ )
+ ));
+ }
+
+ @Test
+ public void documentDistributePayments ( ) throws Exception {
+
+ final PayrollConfiguration payrollConf = DomainObjectGenerator.getPayrollConfiguration();
+ payrollConf.setMainAccountNumber("ABC09876");
+
+ final Customer customer = new Customer();
+ customer.setIdentifier("flauna");
+
+ this.prepareDistributionMocks(customer.getIdentifier(), payrollConf);
+ super.testSubject.setPayrollConfiguration(customer.getIdentifier(), payrollConf);
+ super.eventRecorder.wait(EventConstants.PUT_CONFIGURATION, customer.getIdentifier());
+
+ final PayrollCollectionSheet payrollSheet = new PayrollCollectionSheet();
+ payrollSheet.setSourceAccountNumber("S1R2C3A4C5C6");
+ final PayrollPayment firstPayrollPayment = new PayrollPayment();
+ firstPayrollPayment.setCustomerIdentifier(customer.getIdentifier());
+ firstPayrollPayment.setEmployer("The Shop");
+ firstPayrollPayment.setSalary(BigDecimal.valueOf(1234.56D));
+
+ final PayrollPayment secondPayrollPayment = new PayrollPayment();
+ secondPayrollPayment.setCustomerIdentifier(customer.getIdentifier());
+ secondPayrollPayment.setEmployer("The Tank");
+ secondPayrollPayment.setSalary(BigDecimal.valueOf(14.54D));
+
+ payrollSheet.setPayrollPayments(Lists.newArrayList(firstPayrollPayment, secondPayrollPayment));
+
+ final Account sourceAccount = new Account();
+ sourceAccount.setState(Account.State.OPEN.name());
+ Mockito
+ .doAnswer(invocation -> Optional.of(sourceAccount))
+ .when(this.accountingAdaptor).findAccount(Matchers.eq(payrollSheet.getSourceAccountNumber()));
+
+ Mockito
+ .doAnswer(invocation -> Optional.empty())
+ .when(this.accountingAdaptor).postPayrollPayment(
+ Matchers.any(PayrollCollectionEntity.class),
+ Matchers.refEq(firstPayrollPayment),
+ Matchers.any(PayrollConfiguration.class)
+ );
+
+ Mockito
+ .doAnswer(invocation -> Optional.empty())
+ .when(this.accountingAdaptor).postPayrollPayment(
+ Matchers.any(PayrollCollectionEntity.class),
+ Matchers.refEq(secondPayrollPayment),
+ Matchers.any(PayrollConfiguration.class)
+ );
+
+ Gson gson = new Gson();
+ this.mockMvc.perform(post("/distribution")
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .content(gson.toJson(payrollSheet)))
+ .andExpect(status().isAccepted())
+ .andDo(document("document-distribute-payments", preprocessRequest(prettyPrint()),
+ requestFields(
+ fieldWithPath("sourceAccountNumber").description("Account from which payments ensue"),
+ fieldWithPath("payrollPayments[].customerIdentifier").description("first customer's identifier"),
+ fieldWithPath("payrollPayments[].employer").description("first customer's employer"),
+ fieldWithPath("payrollPayments[].salary").description("first customer's salary"),
+ fieldWithPath("payrollPayments[1].customerIdentifier").description("second customer's identifier"),
+ fieldWithPath("payrollPayments[1].employer").description("second customer's employer"),
+ fieldWithPath("payrollPayments[1].salary").description("second customer's salary")
+ )));
+ }
+
+ @Test
+ public void documentFetchDistributionHistory ( ) throws Exception {
+
+ final PayrollConfiguration payrollConf = DomainObjectGenerator.getPayrollConfiguration();
+ payrollConf.setMainAccountNumber("XYVZ12345");
+
+ final Customer customer = new Customer();
+ customer.setIdentifier("splundna");
+
+ this.prepareDistributionMocks(customer.getIdentifier(), payrollConf);
+ super.testSubject.setPayrollConfiguration(customer.getIdentifier(), payrollConf);
+ super.eventRecorder.wait(EventConstants.PUT_CONFIGURATION, customer.getIdentifier());
+
+ final PayrollCollectionSheet payrollSheet = new PayrollCollectionSheet();
+ payrollSheet.setSourceAccountNumber("S9R7C5A3C1C");
+ final PayrollPayment firstPayrollPayment = new PayrollPayment();
+ firstPayrollPayment.setCustomerIdentifier(customer.getIdentifier());
+ firstPayrollPayment.setEmployer("Awa & Sons");
+ firstPayrollPayment.setSalary(BigDecimal.valueOf(234.56D));
+
+ final PayrollPayment secondPayrollPayment = new PayrollPayment();
+ secondPayrollPayment.setCustomerIdentifier(customer.getIdentifier());
+ secondPayrollPayment.setEmployer("Njeiforbi");
+ secondPayrollPayment.setSalary(BigDecimal.valueOf(120.D));
+
+ payrollSheet.setPayrollPayments(Lists.newArrayList(firstPayrollPayment, secondPayrollPayment));
+
+ final Account sourceAccount = new Account();
+ sourceAccount.setState(Account.State.OPEN.name());
+ Mockito
+ .doAnswer(invocation -> Optional.of(sourceAccount))
+ .when(this.accountingAdaptor).findAccount(Matchers.eq(payrollSheet.getSourceAccountNumber()));
+
+ Mockito
+ .doAnswer(invocation -> Optional.empty())
+ .when(this.accountingAdaptor).postPayrollPayment(
+ Matchers.any(PayrollCollectionEntity.class),
+ Matchers.refEq(firstPayrollPayment),
+ Matchers.any(PayrollConfiguration.class)
+ );
+
+ Mockito
+ .doAnswer(invocation -> Optional.empty())
+ .when(this.accountingAdaptor).postPayrollPayment(
+ Matchers.any(PayrollCollectionEntity.class),
+ Matchers.refEq(secondPayrollPayment),
+ Matchers.any(PayrollConfiguration.class)
+ );
+
+ super.testSubject.distribute(payrollSheet);
+ super.eventRecorder.wait(EventConstants.POST_DISTRIBUTION, payrollSheet.getSourceAccountNumber());
+
+ this.mockMvc.perform(get("/distribution")
+ .accept(MediaType.ALL_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE))
+ .andExpect(status().isOk())
+ .andDo(document("document-fetch-distribution-history", preprocessResponse(prettyPrint()),
+ responseFields(
+ fieldWithPath("[].identifier").description("Payroll history identifier"),
+ fieldWithPath("[].sourceAccountNumber").description("Account from which payments ensue"),
+ fieldWithPath("[].createdBy").description("Employee who distributed payroll"),
+ fieldWithPath("[].createdOn").description("Date when payroll was distributed")
+ )
+ ));
+ }
+
+ @Test
+ public void documentFetchPayments ( ) throws Exception {
+
+ final PayrollConfiguration payrollConf = DomainObjectGenerator.getPayrollConfiguration();
+ payrollConf.setMainAccountNumber("BDK232942");
+
+ final Customer customer = new Customer();
+ customer.setIdentifier("ngone");
+
+ this.prepareDistributionMocks(customer.getIdentifier(), payrollConf);
+ super.testSubject.setPayrollConfiguration(customer.getIdentifier(), payrollConf);
+ super.eventRecorder.wait(EventConstants.PUT_CONFIGURATION, customer.getIdentifier());
+
+ final PayrollCollectionSheet payrollSheet = new PayrollCollectionSheet();
+ payrollSheet.setSourceAccountNumber("O3C87O643E45C4");
+ final PayrollPayment firstPayrollPayment = new PayrollPayment();
+ firstPayrollPayment.setCustomerIdentifier(customer.getIdentifier());
+ firstPayrollPayment.setEmployer("Nkwane");
+ firstPayrollPayment.setSalary(BigDecimal.valueOf(945));
+
+ payrollSheet.setPayrollPayments(Lists.newArrayList(firstPayrollPayment));
+
+ final Account sourceAccount = new Account();
+ sourceAccount.setState(Account.State.OPEN.name());
+ Mockito
+ .doAnswer(invocation -> Optional.of(sourceAccount))
+ .when(this.accountingAdaptor).findAccount(Matchers.eq(payrollSheet.getSourceAccountNumber()));
+
+ Mockito
+ .doAnswer(invocation -> Optional.empty())
+ .when(this.accountingAdaptor).postPayrollPayment(
+ Matchers.any(PayrollCollectionEntity.class),
+ Matchers.refEq(firstPayrollPayment),
+ Matchers.any(PayrollConfiguration.class)
+ );
+
+ super.testSubject.distribute(payrollSheet);
+ super.eventRecorder.wait(EventConstants.POST_DISTRIBUTION, payrollSheet.getSourceAccountNumber());
+
+ final List <PayrollCollectionHistory> payrollCollectionHistories = super.testSubject.fetchDistributionHistory();
+ Assert.assertEquals(1, payrollCollectionHistories.size());
+
+ final PayrollCollectionHistory payrollCollectionHistory = payrollCollectionHistories.get(0);
+ final PayrollPaymentPage payrollPaymentPage =
+ super.testSubject.fetchPayments(payrollCollectionHistory.getIdentifier(), 0, 10, null, null);
+ Assert.assertEquals(Long.valueOf(1L), payrollPaymentPage.getTotalElements());
+
+ this.mockMvc.perform(get("/distribution/" + payrollCollectionHistories.get(0).getIdentifier() + "/payments")
+ .accept(MediaType.ALL_VALUE)
+ .contentType(MediaType.APPLICATION_JSON_VALUE))
+ .andExpect(status().isOk())
+ .andDo(document("document-fetch-payments", preprocessResponse(prettyPrint()),
+ responseFields(
+ fieldWithPath("payrollPayments[0].customerIdentifier").description("second customer's identifier"),
+ fieldWithPath("payrollPayments[0].employer").description("second customer's employer"),
+ fieldWithPath("payrollPayments[0].salary").description("second customer's salary"),
+ fieldWithPath("payrollPayments[0].processed").description("second customer's employer"),
+ fieldWithPath("payrollPayments[0].message").description("second customer's salary"),
+ fieldWithPath("totalPages").type("Integer").description("Pages of payroll payments"),
+ fieldWithPath("totalElements").type("Integer").description("Number of payroll payments")
+ )
+ ));
+ }
+
+ private void prepareConfigurationMocks (final String customerIdentifier, final PayrollConfiguration payrollConfiguration) {
+ Mockito
+ .doAnswer(invocation -> Optional.of(new Customer()))
+ .when(this.customerAdaptor).findCustomer(Matchers.eq(customerIdentifier));
+
+ Mockito
+ .doAnswer(invocation -> Optional.of(new Account()))
+ .when(this.accountingAdaptor).findAccount(Matchers.eq(payrollConfiguration.getMainAccountNumber()));
+
+ payrollConfiguration.getPayrollAllocations().forEach(payrollAllocation ->
+ Mockito
+ .doAnswer(invocation -> Optional.of(new Account()))
+ .when(this.accountingAdaptor).findAccount(Matchers.eq(payrollAllocation.getAccountNumber()))
+ );
+ }
+
+ private void prepareDistributionMocks (final String customerIdentifier, final PayrollConfiguration payrollConfiguration) {
+ Mockito
+ .doAnswer(invocation -> Optional.of(new Customer()))
+ .when(this.customerAdaptor).findCustomer(Matchers.eq(customerIdentifier));
+
+ final Account mainAccount = new Account();
+ mainAccount.setState(Account.State.OPEN.name());
+ Mockito
+ .doAnswer(invocation -> Optional.of(mainAccount))
+ .when(this.accountingAdaptor).findAccount(Matchers.eq(payrollConfiguration.getMainAccountNumber()));
+
+ payrollConfiguration.getPayrollAllocations().forEach(payrollAllocation -> {
+ final Account allocatedAccount = new Account();
+ allocatedAccount.setState(Account.State.OPEN.name());
+ Mockito
+ .doAnswer(invocation -> Optional.of(allocatedAccount))
+ .when(this.accountingAdaptor).findAccount(Matchers.eq(payrollAllocation.getAccountNumber()));
+ });
+ }
+}