blob: 9cb152acf72aad27fddc3bdba56216285826b4f9 [file] [log] [blame]
/**
* 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.portfolio.client.service;
import com.google.gson.JsonElement;
import jakarta.persistence.PersistenceException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandProcessingService;
import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormat;
import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormatRepositoryWrapper;
import org.apache.fineract.infrastructure.accountnumberformat.domain.EntityAccountType;
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksWritePlatformService;
import org.apache.fineract.infrastructure.event.business.domain.client.ClientActivateBusinessEvent;
import org.apache.fineract.infrastructure.event.business.domain.client.ClientCreateBusinessEvent;
import org.apache.fineract.infrastructure.event.business.domain.client.ClientRejectBusinessEvent;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
import org.apache.fineract.organisation.staff.domain.Staff;
import org.apache.fineract.organisation.staff.domain.StaffRepositoryWrapper;
import org.apache.fineract.portfolio.address.service.AddressWritePlatformService;
import org.apache.fineract.portfolio.client.api.ClientApiConstants;
import org.apache.fineract.portfolio.client.data.ClientDataValidator;
import org.apache.fineract.portfolio.client.domain.AccountNumberGenerator;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.client.domain.ClientEnumerations;
import org.apache.fineract.portfolio.client.domain.ClientNonPerson;
import org.apache.fineract.portfolio.client.domain.ClientNonPersonRepositoryWrapper;
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
import org.apache.fineract.portfolio.client.domain.ClientStatus;
import org.apache.fineract.portfolio.client.domain.LegalForm;
import org.apache.fineract.portfolio.client.exception.ClientActiveForUpdateException;
import org.apache.fineract.portfolio.client.exception.ClientHasNoStaffException;
import org.apache.fineract.portfolio.client.exception.ClientMustBePendingToBeDeletedException;
import org.apache.fineract.portfolio.client.exception.InvalidClientSavingProductException;
import org.apache.fineract.portfolio.client.exception.InvalidClientStateTransitionException;
import org.apache.fineract.portfolio.group.domain.Group;
import org.apache.fineract.portfolio.group.domain.GroupRepository;
import org.apache.fineract.portfolio.group.exception.GroupMemberCountNotInPermissibleRangeException;
import org.apache.fineract.portfolio.group.exception.GroupNotFoundException;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.apache.fineract.portfolio.note.domain.Note;
import org.apache.fineract.portfolio.note.domain.NoteRepository;
import org.apache.fineract.portfolio.savings.data.SavingsAccountDataDTO;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrapper;
import org.apache.fineract.portfolio.savings.domain.SavingsProductRepository;
import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException;
import org.apache.fineract.portfolio.savings.service.SavingsApplicationProcessWritePlatformService;
import org.apache.fineract.useradministration.domain.AppUser;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@AllArgsConstructor
@Service
@Slf4j
public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWritePlatformService {
private final PlatformSecurityContext context;
private final ClientRepositoryWrapper clientRepository;
private final ClientNonPersonRepositoryWrapper clientNonPersonRepository;
private final OfficeRepositoryWrapper officeRepositoryWrapper;
private final NoteRepository noteRepository;
private final GroupRepository groupRepository;
private final ClientDataValidator fromApiJsonDeserializer;
private final AccountNumberGenerator accountNumberGenerator;
private final StaffRepositoryWrapper staffRepository;
private final CodeValueRepositoryWrapper codeValueRepository;
private final LoanRepositoryWrapper loanRepositoryWrapper;
private final SavingsAccountRepositoryWrapper savingsRepositoryWrapper;
private final SavingsProductRepository savingsProductRepository;
private final SavingsApplicationProcessWritePlatformService savingsApplicationProcessWritePlatformService;
private final CommandProcessingService commandProcessingService;
private final ConfigurationDomainService configurationDomainService;
private final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository;
private final FromJsonHelper fromApiJsonHelper;
private final AddressWritePlatformService addressWritePlatformService;
private final ClientFamilyMembersWritePlatformService clientFamilyMembersWritePlatformService;
private final BusinessEventNotifierService businessEventNotifierService;
private final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService;
private final ExternalIdFactory externalIdFactory;
@Transactional
@Override
public CommandProcessingResult deleteClient(final Long clientId) {
try {
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
if (client.isNotPending()) {
throw new ClientMustBePendingToBeDeletedException(clientId);
}
final List<Note> relatedNotes = this.noteRepository.findByClient(client);
this.noteRepository.deleteAllInBatch(relatedNotes);
final ClientNonPerson clientNonPerson = this.clientNonPersonRepository.findOneByClientId(clientId);
if (clientNonPerson != null) {
this.clientNonPersonRepository.delete(clientNonPerson);
}
this.clientRepository.delete(client);
this.clientRepository.flush();
return new CommandProcessingResultBuilder() //
.withOfficeId(client.officeId()) //
.withEntityExternalId(client.getExternalId()) //
.withClientId(clientId) //
.withEntityId(clientId) //
.build();
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
log.error("Error occured.", throwable);
throw ErrorHandler.getMappable(dve, "error.msg.client.unknown.data.integrity.issue",
"Unknown data integrity issue with resource.");
}
}
/*
* Guaranteed to throw an exception no matter what the data integrity issue is.
*/
private void handleDataIntegrityIssues(final JsonCommand command, final Throwable realCause, final Exception dve) {
if (realCause.getMessage().contains("external_id")) {
final String externalId = command.stringValueOfParameterNamed("externalId");
throw new PlatformDataIntegrityException("error.msg.client.duplicate.externalId",
"Client with externalId `" + externalId + "` already exists", "externalId", externalId);
} else if (realCause.getMessage().contains("account_no_UNIQUE")) {
final String accountNo = command.stringValueOfParameterNamed("accountNo");
throw new PlatformDataIntegrityException("error.msg.client.duplicate.accountNo",
"Client with accountNo `" + accountNo + "` already exists", "accountNo", accountNo);
} else if (realCause.getMessage().contains("mobile_no")) {
final String mobileNo = command.stringValueOfParameterNamed("mobileNo");
throw new PlatformDataIntegrityException("error.msg.client.duplicate.mobileNo",
"Client with mobileNo `" + mobileNo + "` already exists", "mobileNo", mobileNo);
}
logAsErrorUnexpectedDataIntegrityException(dve);
throw ErrorHandler.getMappable(dve, "error.msg.client.unknown.data.integrity.issue",
"Unknown data integrity issue with resource: " + realCause.getMessage());
}
@Transactional
@Override
public CommandProcessingResult createClient(final JsonCommand command) {
try {
final AppUser currentUser = this.context.authenticatedUser();
this.fromApiJsonDeserializer.validateForCreate(command.json());
final Boolean isAddressEnabled = configurationDomainService.isAddressEnabled();
final Long officeId = command.longValueOfParameterNamed(ClientApiConstants.officeIdParamName);
final Office clientOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId);
final Long groupId = command.longValueOfParameterNamed(ClientApiConstants.groupIdParamName);
Group clientParentGroup = null;
if (groupId != null) {
clientParentGroup = this.groupRepository.findById(groupId).orElseThrow(() -> new GroupNotFoundException(groupId));
}
Staff staff = null;
final Long staffId = command.longValueOfParameterNamed(ClientApiConstants.staffIdParamName);
if (staffId != null) {
staff = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(staffId, clientOffice.getHierarchy());
}
CodeValue gender = null;
final Long genderId = command.longValueOfParameterNamed(ClientApiConstants.genderIdParamName);
if (genderId != null) {
gender = this.codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.GENDER, genderId);
}
CodeValue clientType = null;
final Long clientTypeId = command.longValueOfParameterNamed(ClientApiConstants.clientTypeIdParamName);
if (clientTypeId != null) {
clientType = this.codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.CLIENT_TYPE,
clientTypeId);
}
CodeValue clientClassification = null;
final Long clientClassificationId = command.longValueOfParameterNamed(ClientApiConstants.clientClassificationIdParamName);
if (clientClassificationId != null) {
clientClassification = this.codeValueRepository
.findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.CLIENT_CLASSIFICATION, clientClassificationId);
}
final Long savingsProductId = command.longValueOfParameterNamed(ClientApiConstants.savingsProductIdParamName);
if (savingsProductId != null) {
this.savingsProductRepository.findById(savingsProductId)
.orElseThrow(() -> new SavingsProductNotFoundException(savingsProductId));
}
boolean isEntity = false;
LegalForm legalForm = null;
final Integer legalFormParamValue = command.integerValueOfParameterNamed(ClientApiConstants.legalFormIdParamName);
if (legalFormParamValue != null) {
legalForm = LegalForm.fromInt(legalFormParamValue);
if (legalForm != null) {
isEntity = legalForm.isEntity();
}
}
if (legalForm == null) {
legalForm = LegalForm.PERSON;
}
final String accountNo = command.stringValueOfParameterNamed(ClientApiConstants.accountNoParamName);
final String mobileNo = command.stringValueOfParameterNamed(ClientApiConstants.mobileNoParamName);
final String emailAddress = command.stringValueOfParameterNamed(ClientApiConstants.emailAddressParamName);
final String firstname = command.stringValueOfParameterNamed(ClientApiConstants.firstnameParamName);
final String middlename = command.stringValueOfParameterNamed(ClientApiConstants.middlenameParamName);
final String lastname = command.stringValueOfParameterNamed(ClientApiConstants.lastnameParamName);
final String fullname = command.stringValueOfParameterNamed(ClientApiConstants.fullnameParamName);
final boolean isStaff = command.booleanPrimitiveValueOfParameterNamed(ClientApiConstants.isStaffParamName);
final LocalDate dataOfBirth = command.localDateValueOfParameterNamed(ClientApiConstants.dateOfBirthParamName);
ClientStatus status = ClientStatus.PENDING;
boolean active = false;
if (command.hasParameter("active")) {
active = command.booleanPrimitiveValueOfParameterNamed(ClientApiConstants.activeParamName);
}
LocalDate activationDate = null;
LocalDate officeJoiningDate = null;
if (active) {
status = ClientStatus.ACTIVE;
activationDate = command.localDateValueOfParameterNamed(ClientApiConstants.activationDateParamName);
officeJoiningDate = activationDate;
}
LocalDate submittedOnDate = DateUtils.getBusinessLocalDate();
if (command.hasParameter(ClientApiConstants.submittedOnDateParamName)) {
submittedOnDate = command.localDateValueOfParameterNamed(ClientApiConstants.submittedOnDateParamName);
}
if (active && DateUtils.isAfter(submittedOnDate, activationDate)) {
submittedOnDate = activationDate;
}
final Long savingsAccountId = null;
final ExternalId externalId = externalIdFactory.createFromCommand(command, ClientApiConstants.externalIdParamName);
final Client newClient = Client.instance(currentUser, status, clientOffice, clientParentGroup, accountNo, firstname, middlename,
lastname, fullname, activationDate, officeJoiningDate, externalId, mobileNo, emailAddress, staff, submittedOnDate,
savingsProductId, savingsAccountId, dataOfBirth, gender, clientType, clientClassification, legalForm.getValue(),
isStaff);
this.clientRepository.saveAndFlush(newClient);
boolean rollbackTransaction = false;
if (newClient.isActive()) {
validateParentGroupRulesBeforeClientActivation(newClient);
runEntityDatatableCheck(newClient.getId(), newClient.getLegalForm());
final CommandWrapper commandWrapper = new CommandWrapperBuilder().activateClient(null).build();
rollbackTransaction = this.commandProcessingService.validateRollbackCommand(commandWrapper, currentUser);
}
this.clientRepository.saveAndFlush(newClient);
if (newClient.isAccountNumberRequiresAutoGeneration()) {
AccountNumberFormat accountNumberFormat = this.accountNumberFormatRepository.findByAccountType(EntityAccountType.CLIENT);
newClient.updateAccountNo(accountNumberGenerator.generate(newClient, accountNumberFormat));
this.clientRepository.saveAndFlush(newClient);
}
final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale);
CommandProcessingResult result = openSavingsAccount(newClient, fmt);
if (result.getSavingsId() != null) {
this.clientRepository.saveAndFlush(newClient);
}
if (isEntity) {
extractAndCreateClientNonPerson(newClient, command);
}
if (isAddressEnabled) {
this.addressWritePlatformService.addNewClientAddress(newClient, command);
}
if (command.arrayOfParameterNamed("familyMembers") != null) {
this.clientFamilyMembersWritePlatformService.addClientFamilyMember(newClient, command);
}
if (command.parameterExists(ClientApiConstants.datatables)) {
this.entityDatatableChecksWritePlatformService.saveDatatables(StatusEnum.CREATE.getCode().longValue(),
EntityTables.CLIENT.getName(), newClient.getId(), null,
command.arrayOfParameterNamed(ClientApiConstants.datatables));
}
legalForm = LegalForm.fromInt(newClient.getLegalForm());
entityDatatableChecksWritePlatformService.runTheCheck(newClient.getId(), EntityTables.CLIENT.getName(),
StatusEnum.CREATE.getCode(), EntityTables.CLIENT.getForeignKeyColumnNameOnDatatable(), legalForm.getLabel());
businessEventNotifierService.notifyPostBusinessEvent(new ClientCreateBusinessEvent(newClient));
if (newClient.isActive()) {
businessEventNotifierService.notifyPostBusinessEvent(new ClientActivateBusinessEvent(newClient));
}
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityExternalId(newClient.getExternalId()) //
.withOfficeId(clientOffice.getId()) //
.withClientId(newClient.getId()) //
.withGroupId(groupId) //
.withEntityId(newClient.getId()) //
.withSavingsId(result.getSavingsId())//
.setRollbackTransaction(rollbackTransaction)//
.setRollbackTransaction(result.isRollbackTransaction())//
.build();
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
return CommandProcessingResult.empty();
} catch (final PersistenceException dve) {
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
handleDataIntegrityIssues(command, throwable, dve);
return CommandProcessingResult.empty();
}
}
/**
* This method extracts ClientNonPerson details from Client command and creates a new ClientNonPerson record
*
* @param client
* @param command
*/
public void extractAndCreateClientNonPerson(Client client, JsonCommand command) {
final JsonElement clientNonPersonElement = this.fromApiJsonHelper
.parse(command.jsonFragment(ClientApiConstants.clientNonPersonDetailsParamName));
if (clientNonPersonElement != null && !isEmpty(clientNonPersonElement)) {
final String incorpNumber = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.incorpNumberParamName,
clientNonPersonElement);
final String remarks = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.remarksParamName, clientNonPersonElement);
final LocalDate incorpValidityTill = this.fromApiJsonHelper
.extractLocalDateNamed(ClientApiConstants.incorpValidityTillParamName, clientNonPersonElement);
CodeValue clientNonPersonConstitution = null;
final Long clientNonPersonConstitutionId = this.fromApiJsonHelper.extractLongNamed(ClientApiConstants.constitutionIdParamName,
clientNonPersonElement);
if (clientNonPersonConstitutionId != null) {
clientNonPersonConstitution = this.codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection(
ClientApiConstants.CLIENT_NON_PERSON_CONSTITUTION, clientNonPersonConstitutionId);
}
CodeValue clientNonPersonMainBusinessLine = null;
final Long clientNonPersonMainBusinessLineId = this.fromApiJsonHelper
.extractLongNamed(ClientApiConstants.mainBusinessLineIdParamName, clientNonPersonElement);
if (clientNonPersonMainBusinessLineId != null) {
clientNonPersonMainBusinessLine = this.codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection(
ClientApiConstants.CLIENT_NON_PERSON_MAIN_BUSINESS_LINE, clientNonPersonMainBusinessLineId);
}
final ClientNonPerson newClientNonPerson = ClientNonPerson.createNew(client, clientNonPersonConstitution,
clientNonPersonMainBusinessLine, incorpNumber, incorpValidityTill, remarks);
this.clientNonPersonRepository.save(newClientNonPerson);
}
}
public boolean isEmpty(final JsonElement element) {
return element.toString().trim().length() < 4;
}
@Transactional
@Override
public CommandProcessingResult updateClient(final Long clientId, final JsonCommand command) {
try {
this.fromApiJsonDeserializer.validateForUpdate(command.json());
final Client clientForUpdate = this.clientRepository.findOneWithNotFoundDetection(clientId);
final String clientHierarchy = clientForUpdate.getOffice().getHierarchy();
this.context.validateAccessRights(clientHierarchy);
final Map<String, Object> changes = new LinkedHashMap<>(9);
if (command.isChangeInIntegerParameterNamed(ClientApiConstants.statusParamName, clientForUpdate.getStatus())) {
final Integer newValue = command.integerValueOfParameterNamed(ClientApiConstants.statusParamName);
changes.put(ClientApiConstants.statusParamName, ClientEnumerations.status(newValue));
clientForUpdate.setStatus(ClientStatus.fromInt(newValue).getValue());
}
if (command.isChangeInStringParameterNamed(ClientApiConstants.accountNoParamName, clientForUpdate.getAccountNumber())) {
final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.accountNoParamName);
changes.put(ClientApiConstants.accountNoParamName, newValue);
clientForUpdate.setAccountNumber(StringUtils.defaultIfEmpty(newValue, null));
}
final ExternalId externalId = externalIdFactory.createFromCommand(command, ClientApiConstants.externalIdParamName);
if (command.isChangeInStringParameterNamed(ClientApiConstants.externalIdParamName,
clientForUpdate.getExternalId().getValue())) {
changes.put(ClientApiConstants.externalIdParamName, externalId.getValue());
clientForUpdate.setExternalId(externalId);
}
if (command.isChangeInStringParameterNamed(ClientApiConstants.mobileNoParamName, clientForUpdate.getMobileNo())) {
final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.mobileNoParamName);
changes.put(ClientApiConstants.mobileNoParamName, newValue);
clientForUpdate.setMobileNo(StringUtils.defaultIfEmpty(newValue, null));
}
if (command.isChangeInStringParameterNamed(ClientApiConstants.emailAddressParamName, clientForUpdate.getEmailAddress())) {
final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.emailAddressParamName);
changes.put(ClientApiConstants.emailAddressParamName, newValue);
clientForUpdate.setEmailAddress(StringUtils.defaultIfEmpty(newValue, null));
}
if (command.isChangeInStringParameterNamed(ClientApiConstants.firstnameParamName, clientForUpdate.getFirstname())) {
final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.firstnameParamName);
changes.put(ClientApiConstants.firstnameParamName, newValue);
clientForUpdate.setFirstname(StringUtils.defaultIfEmpty(newValue, null));
}
if (command.isChangeInStringParameterNamed(ClientApiConstants.middlenameParamName, clientForUpdate.getMiddlename())) {
final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.middlenameParamName);
changes.put(ClientApiConstants.middlenameParamName, newValue);
clientForUpdate.setMiddlename(StringUtils.defaultIfEmpty(newValue, null));
}
if (command.isChangeInStringParameterNamed(ClientApiConstants.lastnameParamName, clientForUpdate.getLastname())) {
final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.lastnameParamName);
changes.put(ClientApiConstants.lastnameParamName, newValue);
clientForUpdate.setLastname(StringUtils.defaultIfEmpty(newValue, null));
}
if (command.isChangeInStringParameterNamed(ClientApiConstants.fullnameParamName, clientForUpdate.getFullname())) {
final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.fullnameParamName);
changes.put(ClientApiConstants.fullnameParamName, newValue);
clientForUpdate.setFullname(newValue);
}
if (command.isChangeInLongParameterNamed(ClientApiConstants.staffIdParamName, clientForUpdate.staffId())) {
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.staffIdParamName);
changes.put(ClientApiConstants.staffIdParamName, newValue);
}
if (command.isChangeInLongParameterNamed(ClientApiConstants.genderIdParamName, clientForUpdate.genderId())) {
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.genderIdParamName);
changes.put(ClientApiConstants.genderIdParamName, newValue);
}
if (command.isChangeInLongParameterNamed(ClientApiConstants.savingsProductIdParamName, clientForUpdate.savingsProductId())) {
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.savingsProductIdParamName);
changes.put(ClientApiConstants.savingsProductIdParamName, newValue);
}
if (command.isChangeInLongParameterNamed(ClientApiConstants.clientTypeIdParamName, clientForUpdate.clientTypeId())) {
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.clientTypeIdParamName);
changes.put(ClientApiConstants.clientTypeIdParamName, newValue);
}
if (command.isChangeInLongParameterNamed(ClientApiConstants.clientClassificationIdParamName,
clientForUpdate.clientClassificationId())) {
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.clientClassificationIdParamName);
changes.put(ClientApiConstants.clientClassificationIdParamName, newValue);
}
if (command.isChangeInIntegerParameterNamed(ClientApiConstants.legalFormIdParamName, clientForUpdate.getLegalForm())) {
final Integer newValue = command.integerValueOfParameterNamed(ClientApiConstants.legalFormIdParamName);
if (newValue != null) {
LegalForm legalForm = LegalForm.fromInt(newValue);
if (legalForm != null) {
changes.put(ClientApiConstants.legalFormIdParamName, ClientEnumerations.legalForm(newValue));
clientForUpdate.setLegalForm(legalForm.getValue());
clientForUpdate.resetDerivedNames(legalForm);
} else {
changes.put(ClientApiConstants.legalFormIdParamName, null);
clientForUpdate.setLegalForm(null);
}
} else {
changes.put(ClientApiConstants.legalFormIdParamName, null);
clientForUpdate.setLegalForm(null);
}
}
final String dateFormatAsInput = command.dateFormat();
final String localeAsInput = command.locale();
if (command.isChangeInLocalDateParameterNamed(ClientApiConstants.activationDateParamName,
clientForUpdate.getActivationDate())) {
final String valueAsInput = command.stringValueOfParameterNamed(ClientApiConstants.activationDateParamName);
changes.put(ClientApiConstants.activationDateParamName, valueAsInput);
changes.put(ClientApiConstants.dateFormatParamName, dateFormatAsInput);
changes.put(ClientApiConstants.localeParamName, localeAsInput);
clientForUpdate.setActivationDate(command.localDateValueOfParameterNamed(ClientApiConstants.activationDateParamName));
clientForUpdate.setOfficeJoiningDate(clientForUpdate.getActivationDate());
}
if (command.isChangeInLocalDateParameterNamed(ClientApiConstants.dateOfBirthParamName, clientForUpdate.getDateOfBirth())) {
final String valueAsInput = command.stringValueOfParameterNamed(ClientApiConstants.dateOfBirthParamName);
changes.put(ClientApiConstants.dateOfBirthParamName, valueAsInput);
changes.put(ClientApiConstants.dateFormatParamName, dateFormatAsInput);
changes.put(ClientApiConstants.localeParamName, localeAsInput);
clientForUpdate.setDateOfBirth(command.localDateValueOfParameterNamed(ClientApiConstants.dateOfBirthParamName));
}
if (command.isChangeInLocalDateParameterNamed(ClientApiConstants.submittedOnDateParamName,
clientForUpdate.getSubmittedOnDate())) {
final String valueAsInput = command.stringValueOfParameterNamed(ClientApiConstants.submittedOnDateParamName);
changes.put(ClientApiConstants.submittedOnDateParamName, valueAsInput);
changes.put(ClientApiConstants.dateFormatParamName, dateFormatAsInput);
changes.put(ClientApiConstants.localeParamName, localeAsInput);
clientForUpdate.setSubmittedOnDate(command.localDateValueOfParameterNamed(ClientApiConstants.submittedOnDateParamName));
}
clientForUpdate.validateUpdate();
clientForUpdate.deriveDisplayName();
if (changes.containsKey(ClientApiConstants.staffIdParamName)) {
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.staffIdParamName);
Staff newStaff = null;
if (newValue != null) {
newStaff = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(newValue,
clientForUpdate.getOffice().getHierarchy());
}
clientForUpdate.updateStaff(newStaff);
}
if (changes.containsKey(ClientApiConstants.genderIdParamName)) {
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.genderIdParamName);
CodeValue gender = null;
if (newValue != null) {
gender = this.codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.GENDER, newValue);
}
clientForUpdate.updateGender(gender);
}
if (changes.containsKey(ClientApiConstants.savingsProductIdParamName)) {
if (clientForUpdate.isActive()) {
throw new ClientActiveForUpdateException(clientId, ClientApiConstants.savingsProductIdParamName);
}
final Long savingsProductId = command.longValueOfParameterNamed(ClientApiConstants.savingsProductIdParamName);
if (savingsProductId != null) {
this.savingsProductRepository.findById(savingsProductId)
.orElseThrow(() -> new SavingsProductNotFoundException(savingsProductId));
}
clientForUpdate.updateSavingsProduct(savingsProductId);
}
if (changes.containsKey(ClientApiConstants.genderIdParamName)) {
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.genderIdParamName);
CodeValue newCodeVal = null;
if (newValue != null) {
newCodeVal = this.codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.GENDER, newValue);
}
clientForUpdate.updateGender(newCodeVal);
}
if (changes.containsKey(ClientApiConstants.clientTypeIdParamName)) {
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.clientTypeIdParamName);
CodeValue newCodeVal = null;
if (newValue != null) {
newCodeVal = this.codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.CLIENT_TYPE,
newValue);
}
clientForUpdate.updateClientType(newCodeVal);
}
if (changes.containsKey(ClientApiConstants.clientClassificationIdParamName)) {
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.clientClassificationIdParamName);
CodeValue newCodeVal = null;
if (newValue != null) {
newCodeVal = this.codeValueRepository
.findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.CLIENT_CLASSIFICATION, newValue);
}
clientForUpdate.updateClientClassification(newCodeVal);
}
if (!changes.isEmpty()) {
this.clientRepository.saveAndFlush(clientForUpdate);
}
if (changes.containsKey(ClientApiConstants.legalFormIdParamName)) {
Integer legalFormValue = clientForUpdate.getLegalForm();
boolean isChangedToEntity = false;
if (legalFormValue != null) {
LegalForm legalForm = LegalForm.fromInt(legalFormValue);
if (legalForm != null) {
isChangedToEntity = legalForm.isEntity();
}
}
if (isChangedToEntity) {
extractAndCreateClientNonPerson(clientForUpdate, command);
} else {
final ClientNonPerson clientNonPerson = this.clientNonPersonRepository.findOneByClientId(clientForUpdate.getId());
if (clientNonPerson != null) {
this.clientNonPersonRepository.delete(clientNonPerson);
}
}
}
final ClientNonPerson clientNonPersonForUpdate = this.clientNonPersonRepository.findOneByClientId(clientId);
if (clientNonPersonForUpdate != null) {
final JsonElement clientNonPersonElement = command.jsonElement(ClientApiConstants.clientNonPersonDetailsParamName);
final Map<String, Object> clientNonPersonChanges = clientNonPersonForUpdate
.update(JsonCommand.fromExistingCommand(command, clientNonPersonElement));
if (clientNonPersonChanges.containsKey(ClientApiConstants.constitutionIdParamName)) {
final Long newValue = this.fromApiJsonHelper.extractLongNamed(ClientApiConstants.constitutionIdParamName,
clientNonPersonElement);
CodeValue constitution = null;
if (newValue != null) {
constitution = this.codeValueRepository
.findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.CLIENT_NON_PERSON_CONSTITUTION, newValue);
}
clientNonPersonForUpdate.updateConstitution(constitution);
}
if (clientNonPersonChanges.containsKey(ClientApiConstants.mainBusinessLineIdParamName)) {
final Long newValue = this.fromApiJsonHelper.extractLongNamed(ClientApiConstants.mainBusinessLineIdParamName,
clientNonPersonElement);
CodeValue mainBusinessLine = null;
if (newValue != null) {
mainBusinessLine = this.codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection(
ClientApiConstants.CLIENT_NON_PERSON_MAIN_BUSINESS_LINE, newValue);
}
clientNonPersonForUpdate.updateMainBusinessLine(mainBusinessLine);
}
if (!clientNonPersonChanges.isEmpty()) {
this.clientNonPersonRepository.saveAndFlush(clientNonPersonForUpdate);
}
changes.putAll(clientNonPersonChanges);
} else {
final Integer legalFormParamValue = command.integerValueOfParameterNamed(ClientApiConstants.legalFormIdParamName);
boolean isEntity = false;
if (legalFormParamValue != null) {
final LegalForm legalForm = LegalForm.fromInt(legalFormParamValue);
if (legalForm != null) {
isEntity = legalForm.isEntity();
}
}
if (isEntity) {
extractAndCreateClientNonPerson(clientForUpdate, command);
}
}
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityExternalId(clientForUpdate.getExternalId()) //
.withOfficeId(clientForUpdate.officeId()) //
.withClientId(clientId) //
.withEntityId(clientId) //
.with(changes) //
.build();
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
return CommandProcessingResult.empty();
} catch (final PersistenceException dve) {
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
handleDataIntegrityIssues(command, throwable, dve);
return CommandProcessingResult.empty();
}
}
@Transactional
@Override
public CommandProcessingResult activateClient(final Long clientId, final JsonCommand command) {
try {
this.fromApiJsonDeserializer.validateActivation(command);
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId, true);
validateParentGroupRulesBeforeClientActivation(client);
final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale);
final LocalDate activationDate = command.localDateValueOfParameterNamed("activationDate");
runEntityDatatableCheck(clientId, client.getLegalForm());
final AppUser currentUser = this.context.authenticatedUser();
client.activate(currentUser, fmt, activationDate);
CommandProcessingResult result = openSavingsAccount(client, fmt);
clientRepository.saveAndFlush(client);
businessEventNotifierService.notifyPostBusinessEvent(new ClientActivateBusinessEvent(client));
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withOfficeId(client.officeId()) //
.withEntityExternalId(client.getExternalId()) //
.withClientId(clientId) //
.withEntityId(clientId) //
.withSavingsId(result.getSavingsId())//
.setRollbackTransaction(result.isRollbackTransaction())//
.build();
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
return CommandProcessingResult.empty();
}
}
private CommandProcessingResult openSavingsAccount(final Client client, final DateTimeFormatter fmt) {
CommandProcessingResult commandProcessingResult = CommandProcessingResult.empty();
if (client.isActive() && client.savingsProductId() != null) {
SavingsAccountDataDTO savingsAccountDataDTO = new SavingsAccountDataDTO(client, null, client.savingsProductId(),
client.getActivationDate(), client.activatedBy(), fmt);
commandProcessingResult = this.savingsApplicationProcessWritePlatformService.createActiveApplication(savingsAccountDataDTO);
if (commandProcessingResult.getSavingsId() != null) {
this.savingsRepositoryWrapper.findOneWithNotFoundDetection(commandProcessingResult.getSavingsId());
client.updateSavingsAccount(commandProcessingResult.getSavingsId());
client.updateSavingsProduct(null);
}
}
return commandProcessingResult;
}
private void logAsErrorUnexpectedDataIntegrityException(final Exception dve) {
log.error("Error occured.", dve);
}
@Transactional
@Override
public CommandProcessingResult unassignClientStaff(final Long clientId, final JsonCommand command) {
this.context.authenticatedUser();
final Map<String, Object> actualChanges = new LinkedHashMap<>(5);
this.fromApiJsonDeserializer.validateForUnassignStaff(command.json());
final Client clientForUpdate = this.clientRepository.findOneWithNotFoundDetection(clientId);
final Staff presentStaff = clientForUpdate.getStaff();
Long presentStaffId = null;
if (presentStaff == null) {
throw new ClientHasNoStaffException(clientId);
}
presentStaffId = presentStaff.getId();
final String staffIdParamName = ClientApiConstants.staffIdParamName;
if (!command.isChangeInLongParameterNamed(staffIdParamName, presentStaffId)) {
clientForUpdate.unassignStaff();
}
this.clientRepository.saveAndFlush(clientForUpdate);
actualChanges.put(staffIdParamName, presentStaffId);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withOfficeId(clientForUpdate.officeId()) //
.withEntityId(clientForUpdate.getId()) //
.withEntityExternalId(clientForUpdate.getExternalId()) //
.withClientId(clientId) //
.with(actualChanges) //
.build();
}
@Override
public CommandProcessingResult assignClientStaff(final Long clientId, final JsonCommand command) {
this.context.authenticatedUser();
final Map<String, Object> actualChanges = new LinkedHashMap<>(5);
this.fromApiJsonDeserializer.validateForAssignStaff(command.json());
final Client clientForUpdate = this.clientRepository.findOneWithNotFoundDetection(clientId);
Staff staff = null;
final Long staffId = command.longValueOfParameterNamed(ClientApiConstants.staffIdParamName);
if (staffId != null) {
staff = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(staffId, clientForUpdate.getOffice().getHierarchy());
/**
* TODO Vishwas: We maintain history of chage of loan officer w.r.t loan in a history table, should we do
* the same for a client? Especially useful when the change happens due to a transfer etc
**/
clientForUpdate.assignStaff(staff);
}
this.clientRepository.saveAndFlush(clientForUpdate);
actualChanges.put(ClientApiConstants.staffIdParamName, staffId);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withOfficeId(clientForUpdate.officeId()) //
.withEntityExternalId(clientForUpdate.getExternalId()) //
.withEntityId(clientForUpdate.getId()) //
.withClientId(clientId) //
.with(actualChanges) //
.build();
}
@Transactional
@Override
public CommandProcessingResult closeClient(final Long clientId, final JsonCommand command) {
try {
final AppUser currentUser = this.context.authenticatedUser();
this.fromApiJsonDeserializer.validateClose(command);
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
final LocalDate closureDate = command.localDateValueOfParameterNamed(ClientApiConstants.closureDateParamName);
final Long closureReasonId = command.longValueOfParameterNamed(ClientApiConstants.closureReasonIdParamName);
final CodeValue closureReason = this.codeValueRepository
.findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.CLIENT_CLOSURE_REASON, closureReasonId);
if (ClientStatus.fromInt(client.getStatus()).isClosed()) {
final String errorMessage = "Client is already closed.";
throw new InvalidClientStateTransitionException("close", "is.already.closed", errorMessage);
} else if (ClientStatus.fromInt(client.getStatus()).isUnderTransfer()) {
final String errorMessage = "Cannot Close a Client under Transfer";
throw new InvalidClientStateTransitionException("close", "is.under.transfer", errorMessage);
}
if (client.isNotPending() && DateUtils.isAfter(client.getActivationDate(), closureDate)) {
final String errorMessage = "The client closureDate cannot be before the client ActivationDate.";
throw new InvalidClientStateTransitionException("close", "date.cannot.before.client.actvation.date", errorMessage,
closureDate, client.getActivationDate());
}
final LegalForm legalForm = LegalForm.fromInt(client.getLegalForm());
entityDatatableChecksWritePlatformService.runTheCheck(clientId, EntityTables.CLIENT.getName(), StatusEnum.CLOSE.getCode(),
EntityTables.CLIENT.getForeignKeyColumnNameOnDatatable(), legalForm.getLabel());
final List<Loan> clientLoans = this.loanRepositoryWrapper.findLoanByClientId(clientId);
for (final Loan loan : clientLoans) {
final LoanStatusMapper loanStatus = new LoanStatusMapper(loan.getStatus().getValue());
if (loanStatus.isOpen() || loanStatus.isPendingApproval() || loanStatus.isAwaitingDisbursal()) {
final String errorMessage = "Client cannot be closed because of non-closed loans.";
throw new InvalidClientStateTransitionException("close", "loan.non-closed", errorMessage);
} else if (loanStatus.isClosed() && DateUtils.isAfter(loan.getClosedOnDate(), closureDate)) {
final String errorMessage = "The client closureDate cannot be before the loan closedOnDate.";
throw new InvalidClientStateTransitionException("close", "date.cannot.before.loan.closed.date", errorMessage,
closureDate, loan.getClosedOnDate());
} else if (loanStatus.isOverpaid()) {
final String errorMessage = "Client cannot be closed because of overpaid loans.";
throw new InvalidClientStateTransitionException("close", "loan.overpaid", errorMessage);
}
}
final List<SavingsAccount> clientSavingAccounts = this.savingsRepositoryWrapper.findSavingAccountByClientId(clientId);
for (final SavingsAccount saving : clientSavingAccounts) {
if (saving.isActive() || saving.isSubmittedAndPendingApproval() || saving.isApproved()) {
final String errorMessage = "Client cannot be closed because of non-closed savings account.";
throw new InvalidClientStateTransitionException("close", "non-closed.savings.account", errorMessage);
}
}
client.close(currentUser, closureReason, closureDate);
this.clientRepository.saveAndFlush(client);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withClientId(clientId) //
.withEntityId(clientId) //
.withEntityExternalId(client.getExternalId()) //
.build();
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
return CommandProcessingResult.empty();
}
}
@Override
public CommandProcessingResult updateDefaultSavingsAccount(final Long clientId, final JsonCommand command) {
this.context.authenticatedUser();
final Map<String, Object> actualChanges = new LinkedHashMap<>(5);
this.fromApiJsonDeserializer.validateForSavingsAccount(command.json());
final Client clientForUpdate = this.clientRepository.findOneWithNotFoundDetection(clientId);
SavingsAccount savingsAccount = null;
final Long savingsId = command.longValueOfParameterNamed(ClientApiConstants.savingsAccountIdParamName);
if (savingsId != null) {
savingsAccount = this.savingsRepositoryWrapper.findOneWithNotFoundDetection(savingsId);
if (!savingsAccount.getClient().identifiedBy(clientId)) {
String defaultUserMessage = "saving account must belongs to client";
throw new InvalidClientSavingProductException("saving.account", "must.belongs.to.client", defaultUserMessage, savingsId,
clientForUpdate.getId());
}
clientForUpdate.updateSavingsAccount(savingsId);
}
this.clientRepository.saveAndFlush(clientForUpdate);
actualChanges.put(ClientApiConstants.savingsAccountIdParamName, savingsId);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withOfficeId(clientForUpdate.officeId()) //
.withEntityId(clientForUpdate.getId()) //
.withEntityExternalId(clientForUpdate.getExternalId()) //
.withClientId(clientId) //
.with(actualChanges) //
.build();
}
/*
* To become a part of a group, group may have set of criteria to be m et before client can become member of it.
*/
private void validateParentGroupRulesBeforeClientActivation(Client client) {
Integer minNumberOfClients = configurationDomainService.retrieveMinAllowedClientsInGroup();
Integer maxNumberOfClients = configurationDomainService.retrieveMaxAllowedClientsInGroup();
if (client.getGroups() != null && maxNumberOfClients != null) {
for (Group group : client.getGroups()) {
/**
* Since this Client has not yet been associated with the group, reduce maxNumberOfClients by 1
**/
final boolean validationsuccess = group.isGroupsClientCountWithinMaxRange(maxNumberOfClients - 1);
if (!validationsuccess) {
throw new GroupMemberCountNotInPermissibleRangeException(group.getId(), minNumberOfClients, maxNumberOfClients);
}
}
}
}
private void runEntityDatatableCheck(final Long clientId, final Integer legalFormId) {
final LegalForm legalForm = LegalForm.fromInt(legalFormId);
entityDatatableChecksWritePlatformService.runTheCheck(clientId, EntityTables.CLIENT.getName(), StatusEnum.ACTIVATE.getCode(),
EntityTables.CLIENT.getForeignKeyColumnNameOnDatatable(), legalForm.getLabel());
}
@Override
public CommandProcessingResult rejectClient(final Long entityId, final JsonCommand command) {
final AppUser currentUser = this.context.authenticatedUser();
this.fromApiJsonDeserializer.validateRejection(command);
final Client client = this.clientRepository.findOneWithNotFoundDetection(entityId);
final LocalDate rejectionDate = command.localDateValueOfParameterNamed(ClientApiConstants.rejectionDateParamName);
final Long rejectionReasonId = command.longValueOfParameterNamed(ClientApiConstants.rejectionReasonIdParamName);
final CodeValue rejectionReason = this.codeValueRepository
.findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.CLIENT_REJECT_REASON, rejectionReasonId);
if (client.isNotPending()) {
final String errorMessage = "Only clients pending activation may be withdrawn.";
throw new InvalidClientStateTransitionException("rejection", "on.account.not.in.pending.activation.status", errorMessage,
rejectionDate, client.getSubmittedOnDate());
} else if (DateUtils.isAfter(client.getSubmittedOnDate(), rejectionDate)) {
final String errorMessage = "The client rejection date cannot be before the client submitted date.";
throw new InvalidClientStateTransitionException("rejection", "date.cannot.before.client.submitted.date", errorMessage,
rejectionDate, client.getSubmittedOnDate());
}
client.reject(currentUser, rejectionReason, rejectionDate);
clientRepository.saveAndFlush(client);
businessEventNotifierService.notifyPostBusinessEvent(new ClientRejectBusinessEvent(client));
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withClientId(entityId) //
.withEntityId(entityId) //
.withEntityExternalId(client.getExternalId()) //
.build();
}
@Override
public CommandProcessingResult withdrawClient(Long entityId, JsonCommand command) {
final AppUser currentUser = this.context.authenticatedUser();
this.fromApiJsonDeserializer.validateWithdrawn(command);
final Client client = this.clientRepository.findOneWithNotFoundDetection(entityId);
final LocalDate withdrawalDate = command.localDateValueOfParameterNamed(ClientApiConstants.withdrawalDateParamName);
final Long withdrawalReasonId = command.longValueOfParameterNamed(ClientApiConstants.withdrawalReasonIdParamName);
final CodeValue withdrawalReason = this.codeValueRepository
.findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.CLIENT_WITHDRAW_REASON, withdrawalReasonId);
if (client.isNotPending()) {
final String errorMessage = "Only clients pending activation may be withdrawn.";
throw new InvalidClientStateTransitionException("withdrawal", "on.account.not.in.pending.activation.status", errorMessage,
withdrawalDate, client.getSubmittedOnDate());
} else if (DateUtils.isAfter(client.getSubmittedOnDate(), withdrawalDate)) {
final String errorMessage = "The client withdrawal date cannot be before the client submitted date.";
throw new InvalidClientStateTransitionException("withdrawal", "date.cannot.before.client.submitted.date", errorMessage,
withdrawalDate, client.getSubmittedOnDate());
}
client.withdraw(currentUser, withdrawalReason, withdrawalDate);
this.clientRepository.saveAndFlush(client);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withClientId(entityId) //
.withEntityId(entityId) //
.withEntityExternalId(client.getExternalId()) //
.build();
}
@Override
public CommandProcessingResult reActivateClient(Long entityId, JsonCommand command) {
final AppUser currentUser = this.context.authenticatedUser();
this.fromApiJsonDeserializer.validateReactivate(command);
final Client client = this.clientRepository.findOneWithNotFoundDetection(entityId);
final LocalDate reactivateDate = command.localDateValueOfParameterNamed(ClientApiConstants.reactivationDateParamName);
if (!client.isClosed()) {
final String errorMessage = "only closed clients may be reactivated.";
throw new InvalidClientStateTransitionException("reactivation", "on.nonclosed.account", errorMessage);
} else if (DateUtils.isAfter(client.getClosureDate(), reactivateDate)) {
final String errorMessage = "The client reactivation date cannot be before the client closed date.";
throw new InvalidClientStateTransitionException("reactivation", "date.cannot.before.client.closed.date", errorMessage,
reactivateDate, client.getClosureDate());
}
client.reActivate(currentUser, reactivateDate);
this.clientRepository.saveAndFlush(client);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withClientId(entityId) //
.withEntityId(entityId) //
.withEntityExternalId(client.getExternalId()) //
.build();
}
@Override
public CommandProcessingResult undoRejection(Long entityId, JsonCommand command) {
final AppUser currentUser = this.context.authenticatedUser();
this.fromApiJsonDeserializer.validateUndoRejection(command);
final Client client = this.clientRepository.findOneWithNotFoundDetection(entityId);
final LocalDate undoRejectDate = command.localDateValueOfParameterNamed(ClientApiConstants.reopenedDateParamName);
if (!client.isRejected()) {
final String errorMessage = "only rejected clients may be reactivated.";
throw new InvalidClientStateTransitionException("undorejection", "on.nonrejected.account", errorMessage);
} else if (DateUtils.isAfter(client.getRejectedDate(), undoRejectDate)) {
final String errorMessage = "The client reactivation date cannot be before the client rejected date.";
throw new InvalidClientStateTransitionException("reopened", "date.cannot.before.client.rejected.date", errorMessage,
undoRejectDate, client.getRejectedDate());
}
client.reOpened(currentUser, undoRejectDate);
this.clientRepository.saveAndFlush(client);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withClientId(entityId) //
.withEntityId(entityId) //
.withEntityExternalId(client.getExternalId()) //
.build();
}
@Override
public CommandProcessingResult undoWithdrawal(Long entityId, JsonCommand command) {
final AppUser currentUser = this.context.authenticatedUser();
this.fromApiJsonDeserializer.validateUndoWithDrawn(command);
final Client client = this.clientRepository.findOneWithNotFoundDetection(entityId);
final LocalDate undoWithdrawalDate = command.localDateValueOfParameterNamed(ClientApiConstants.reopenedDateParamName);
if (!client.isWithdrawn()) {
final String errorMessage = "only withdrawal clients may be reactivated.";
throw new InvalidClientStateTransitionException("undoWithdrawal", "on.nonwithdrawal.account", errorMessage);
} else if (DateUtils.isAfter(client.getWithdrawalDate(), undoWithdrawalDate)) {
final String errorMessage = "The client reactivation date cannot be before the client withdrawal date.";
throw new InvalidClientStateTransitionException("reopened", "date.cannot.before.client.withdrawal.date", errorMessage,
undoWithdrawalDate, client.getWithdrawalDate());
}
client.reOpened(currentUser, undoWithdrawalDate);
this.clientRepository.saveAndFlush(client);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withClientId(entityId) //
.withEntityId(entityId) //
.withEntityExternalId(client.getExternalId()) //
.build();
}
}