Skip to content
Permalink
Browse files
Merge pull request #4 from markusgeiss/develop
added account validation before initializing payroll transaction
  • Loading branch information
markusgeiss committed Oct 6, 2017
2 parents 468e43f + 23bd6e3 commit a797401e2382ed4d849bac26a910f9317f210fed
Showing 3 changed files with 128 additions and 21 deletions.
@@ -69,7 +69,8 @@ void setPayrollConfiguration(@PathVariable(value = "identifier") final String cu
consumes = MediaType.APPLICATION_JSON_VALUE
)
@ThrowsExceptions({
@ThrowsException(status = HttpStatus.BAD_REQUEST, exception = PayrollPaymentValidationException.class)
@ThrowsException(status = HttpStatus.BAD_REQUEST, exception = PayrollPaymentValidationException.class),
@ThrowsException(status = HttpStatus.CONFLICT, exception = PayrollPaymentValidationException.class)
})
void distribute(@RequestBody @Valid final PayrollCollectionSheet payrollCollectionSheet);

@@ -19,6 +19,8 @@
import io.mifos.accounting.api.v1.domain.Account;
import io.mifos.customer.api.v1.domain.Customer;
import io.mifos.payroll.api.v1.EventConstants;
import io.mifos.payroll.api.v1.client.PayrollPaymentValidationException;
import io.mifos.payroll.api.v1.domain.PayrollAllocation;
import io.mifos.payroll.api.v1.domain.PayrollCollectionHistory;
import io.mifos.payroll.api.v1.domain.PayrollCollectionSheet;
import io.mifos.payroll.api.v1.domain.PayrollConfiguration;
@@ -67,17 +69,19 @@ public void shouldDistributePayments() throws Exception {
payrollPayment.setSalary(BigDecimal.valueOf(1234.56D));
payrollCollectionSheet.setPayrollPayments(Lists.newArrayList(payrollPayment));

final Account sourceAccount = new Account();
sourceAccount.setState(Account.State.OPEN.name());
Mockito
.doAnswer(invocation -> Optional.of(new Account()))
.doAnswer(invocation -> Optional.of(sourceAccount))
.when(this.accountingAdaptorSpy).findAccount(Matchers.eq(payrollCollectionSheet.getSourceAccountNumber()));

Mockito
.doAnswer(invocation -> Optional.empty())
.when(this.accountingAdaptorSpy).postPayrollPayment(
Matchers.any(PayrollCollectionEntity.class),
Matchers.refEq(payrollPayment),
Matchers.any(PayrollConfiguration.class)
);
Matchers.any(PayrollCollectionEntity.class),
Matchers.refEq(payrollPayment),
Matchers.any(PayrollConfiguration.class)
);

super.testSubject.distribute(payrollCollectionSheet);
Assert.assertTrue(super.eventRecorder.wait(EventConstants.POST_DISTRIBUTION, payrollCollectionSheet.getSourceAccountNumber()));
@@ -94,20 +98,103 @@ public void shouldDistributePayments() throws Exception {
Assert.assertTrue(fetchedPayrollPayment.getProcessed());
}

@Test(expected = PayrollPaymentValidationException.class)
public void shouldNotDistributePaymentsAllocatedAccountClosed() throws Exception {
final String customerIdentifier = RandomStringUtils.randomAlphanumeric(32);
final PayrollConfiguration payrollConfiguration = DomainObjectGenerator.getPayrollConfiguration();
this.prepareMocks(customerIdentifier, payrollConfiguration);

final PayrollAllocation invalidPayrollAllocation = new PayrollAllocation();
invalidPayrollAllocation.setAccountNumber(RandomStringUtils.randomAlphanumeric(34));
invalidPayrollAllocation.setProportional(Boolean.FALSE);
invalidPayrollAllocation.setAmount(BigDecimal.valueOf(200.00D));
payrollConfiguration.getPayrollAllocations().add(invalidPayrollAllocation);

final Account invalidPayrollAccount = new Account();
invalidPayrollAccount.setState(Account.State.CLOSED.name());
Mockito
.doAnswer(invocation -> Optional.of(invalidPayrollAccount))
.when(this.accountingAdaptorSpy).findAccount(Matchers.eq(invalidPayrollAllocation.getAccountNumber()));

super.testSubject.setPayrollConfiguration(customerIdentifier, payrollConfiguration);
Assert.assertTrue(super.eventRecorder.wait(EventConstants.PUT_CONFIGURATION, customerIdentifier));

final PayrollCollectionSheet payrollCollectionSheet = new PayrollCollectionSheet();
payrollCollectionSheet.setSourceAccountNumber(RandomStringUtils.randomAlphanumeric(34));
final PayrollPayment payrollPayment = new PayrollPayment();
payrollPayment.setCustomerIdentifier(customerIdentifier);
payrollPayment.setEmployer("ACME, Inc.");
payrollPayment.setSalary(BigDecimal.valueOf(1234.56D));
payrollCollectionSheet.setPayrollPayments(Lists.newArrayList(payrollPayment));

final Account sourceAccount = new Account();
sourceAccount.setState(Account.State.OPEN.name());
Mockito
.doAnswer(invocation -> Optional.of(sourceAccount))
.when(this.accountingAdaptorSpy).findAccount(Matchers.eq(payrollCollectionSheet.getSourceAccountNumber()));

Mockito
.doAnswer(invocation -> Optional.empty())
.when(this.accountingAdaptorSpy).postPayrollPayment(
Matchers.any(PayrollCollectionEntity.class),
Matchers.refEq(payrollPayment),
Matchers.any(PayrollConfiguration.class)
);

super.testSubject.distribute(payrollCollectionSheet);
}

@Test(expected = PayrollPaymentValidationException.class)
public void shouldNotDistributePaymentsSourceAccountClosed() throws Exception {
final String customerIdentifier = RandomStringUtils.randomAlphanumeric(32);
final PayrollConfiguration payrollConfiguration = DomainObjectGenerator.getPayrollConfiguration();
this.prepareMocks(customerIdentifier, payrollConfiguration);

super.testSubject.setPayrollConfiguration(customerIdentifier, payrollConfiguration);
Assert.assertTrue(super.eventRecorder.wait(EventConstants.PUT_CONFIGURATION, customerIdentifier));

final PayrollCollectionSheet payrollCollectionSheet = new PayrollCollectionSheet();
payrollCollectionSheet.setSourceAccountNumber(RandomStringUtils.randomAlphanumeric(34));
final PayrollPayment payrollPayment = new PayrollPayment();
payrollPayment.setCustomerIdentifier(customerIdentifier);
payrollPayment.setEmployer("ACME, Inc.");
payrollPayment.setSalary(BigDecimal.valueOf(1234.56D));
payrollCollectionSheet.setPayrollPayments(Lists.newArrayList(payrollPayment));

final Account sourceAccount = new Account();
sourceAccount.setState(Account.State.CLOSED.name());
Mockito
.doAnswer(invocation -> Optional.of(sourceAccount))
.when(this.accountingAdaptorSpy).findAccount(Matchers.eq(payrollCollectionSheet.getSourceAccountNumber()));

Mockito
.doAnswer(invocation -> Optional.empty())
.when(this.accountingAdaptorSpy).postPayrollPayment(
Matchers.any(PayrollCollectionEntity.class),
Matchers.refEq(payrollPayment),
Matchers.any(PayrollConfiguration.class)
);

super.testSubject.distribute(payrollCollectionSheet);
}

private void prepareMocks(final String customerIdentifier, final PayrollConfiguration payrollConfiguration) {
Mockito
.doAnswer(invocation -> Optional.of(new Customer()))
.when(this.customerAdaptorSpy).findCustomer(Matchers.eq(customerIdentifier));

final Account mainAccount = new Account();
mainAccount.setState(Account.State.OPEN.name());
Mockito
.doAnswer(invocation -> Optional.of(new Account()))
.doAnswer(invocation -> Optional.of(mainAccount))
.when(this.accountingAdaptorSpy).findAccount(Matchers.eq(payrollConfiguration.getMainAccountNumber()));

payrollConfiguration.getPayrollAllocations().forEach(payrollAllocation ->
Mockito
.doAnswer(invocation -> Optional.of(new Account()))
.when(this.accountingAdaptorSpy).findAccount(Matchers.eq(payrollAllocation.getAccountNumber()))
);
payrollConfiguration.getPayrollAllocations().forEach(payrollAllocation -> {
final Account allocatedAccount = new Account();
allocatedAccount.setState(Account.State.OPEN.name());
Mockito
.doAnswer(invocation -> Optional.of(allocatedAccount))
.when(this.accountingAdaptorSpy).findAccount(Matchers.eq(payrollAllocation.getAccountNumber()));
});
}

}
@@ -15,6 +15,7 @@
*/
package io.mifos.payroll.service.rest;

import io.mifos.accounting.api.v1.domain.Account;
import io.mifos.anubis.annotation.AcceptedTokenType;
import io.mifos.anubis.annotation.Permittable;
import io.mifos.anubis.annotation.Permittables;
@@ -23,11 +24,13 @@
import io.mifos.payroll.api.v1.PermittableGroupIds;
import io.mifos.payroll.api.v1.domain.PayrollCollectionHistory;
import io.mifos.payroll.api.v1.domain.PayrollCollectionSheet;
import io.mifos.payroll.api.v1.domain.PayrollConfiguration;
import io.mifos.payroll.api.v1.domain.PayrollPaymentPage;
import io.mifos.payroll.service.ServiceConstants;
import io.mifos.payroll.service.internal.command.DistributePayrollCommand;
import io.mifos.payroll.service.internal.service.PayrollConfigurationService;
import io.mifos.payroll.service.internal.service.PayrollDistributionService;
import io.mifos.payroll.service.internal.service.adaptor.AccountingAdaptor;
import io.mifos.payroll.service.rest.util.PageableBuilder;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
@@ -53,17 +56,20 @@ public class PayrollDistributionRestController {
private final CommandGateway commandGateway;
private final PayrollDistributionService payrollDistributionService;
private final PayrollConfigurationService payrollConfigurationService;
private final AccountingAdaptor accountingAdaptor;

@Autowired
public PayrollDistributionRestController(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
final CommandGateway commandGateway,
final PayrollDistributionService payrollDistributionService,
final PayrollConfigurationService payrollConfigurationService) {
final PayrollConfigurationService payrollConfigurationService,
final AccountingAdaptor accountingAdaptor) {
super();
this.logger = logger;
this.commandGateway = commandGateway;
this.payrollDistributionService = payrollDistributionService;
this.payrollConfigurationService = payrollConfigurationService;
this.accountingAdaptor = accountingAdaptor;
}

@Permittables({
@@ -81,14 +87,17 @@ public PayrollDistributionRestController(@Qualifier(ServiceConstants.LOGGER_NAME
@ResponseBody
public ResponseEntity<Void> distribute(@RequestBody @Valid final PayrollCollectionSheet payrollCollectionSheet) {

this.payrollConfigurationService.findAccount(payrollCollectionSheet.getSourceAccountNumber())
.orElseThrow(() -> ServiceException.notFound("Account {0} not available.", payrollCollectionSheet.getSourceAccountNumber()));
this.verifyAccount(payrollCollectionSheet.getSourceAccountNumber());
payrollCollectionSheet.getPayrollPayments()
.forEach(payrollPayment -> {
final PayrollConfiguration payrollConfiguration =
this.payrollConfigurationService.findPayrollConfiguration(payrollPayment.getCustomerIdentifier())
.orElseThrow(() -> ServiceException.conflict("Payroll configuration for certain customers not available."));

if (payrollCollectionSheet.getPayrollPayments()
.stream().anyMatch(payrollPayment ->
!this.payrollConfigurationService.findPayrollConfiguration(payrollPayment.getCustomerIdentifier()).isPresent())) {
throw ServiceException.conflict("Payroll configuration for certain customers not available.");
}
this.verifyAccount(payrollConfiguration.getMainAccountNumber());
payrollConfiguration.getPayrollAllocations()
.forEach(payrollAllocation -> this.verifyAccount(payrollAllocation.getAccountNumber()));
});

this.commandGateway.process(new DistributePayrollCommand(payrollCollectionSheet));

@@ -138,4 +147,14 @@ ResponseEntity<PayrollPaymentPage> fetchPayments(
return ResponseEntity.ok(this.payrollDistributionService
.fetchPayments(identifier, PageableBuilder.create(pageIndex, size, sortColumn, sortDirection)));
}

private void verifyAccount(final String accountIdentifier) {
final Account account = this.accountingAdaptor.findAccount(accountIdentifier).orElseThrow(
() -> ServiceException.conflict("Account {0} not found.")
);

if (!account.getState().equals(Account.State.OPEN.name())) {
throw ServiceException.conflict("Account {0} must be open.", accountIdentifier);
}
}
}

0 comments on commit a797401

Please sign in to comment.