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()));
+    });
+  }
+}