ptuomola commented on a change in pull request #1770:
URL: https://github.com/apache/fineract/pull/1770#discussion_r698477273
##########
File path:
fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
##########
@@ -1814,6 +1822,32 @@ public CommandWrapperBuilder updateCollateral(final Long
loanId, final Long coll
return this;
}
+ public CommandWrapperBuilder updateCollateralProduct(final Long
collateralId) {
+ this.actionName = "UPDATE";
+ this.entityName = "COLLATERAL_PRODUCT";
+ this.entityId = collateralId;
+ this.href = "/collateral-management/" + collateralId;
+ return this;
+ }
+
+ public CommandWrapperBuilder updateClientCollateralProduct(final Long
clientId, final Long collateralId) {
+ this.actionName = "UPDATE";
+ this.entityName = "CLIENT_COLLATERAL_PRODUCT";
+ this.entityId = collateralId;
+ this.clientId = clientId;
+ this.href = "/clients/" + clientId + "/collateral" + collateralId;
+ return this;
Review comment:
Should there be a "/" before the collateralId as a separator?
##########
File path:
fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
##########
@@ -1823,6 +1857,31 @@ public CommandWrapperBuilder deleteCollateral(final Long
loanId, final Long coll
return this;
}
+ public CommandWrapperBuilder deleteCollateralProduct(final Long
collateralId) {
+ this.actionName = "DELETE";
+ this.entityName = "COLLATERAL_PRODUCT";
+ this.entityId = collateralId;
+ this.href = "/collateral-management/delete/" + collateralId;
+ return this;
+ }
+
+ public CommandWrapperBuilder deleteClientCollateralProduct(final Long
collateralId, final Long clientId) {
+ this.actionName = "DELETE";
+ this.entityName = "CLIENT_COLLATERAL_PRODUCT";
+ this.entityId = collateralId;
+ this.clientId = clientId;
+ this.href = "/collateral-management/" + collateralId + "/clients/" +
clientId;
+ return this;
+ }
+
+ public CommandWrapperBuilder addClientCollateralProduct(final Long
clientId) {
+ this.actionName = "CREATE";
+ this.entityName = "CLIENT_COLLATERAL_PRODUCT";
+ this.clientId = clientId;
+ this.href = "/collateral/clients/" + clientId + "/create";
+ return this;
Review comment:
Same as above - should this not be consistent?
##########
File path:
fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
##########
@@ -1823,6 +1857,31 @@ public CommandWrapperBuilder deleteCollateral(final Long
loanId, final Long coll
return this;
}
+ public CommandWrapperBuilder deleteCollateralProduct(final Long
collateralId) {
+ this.actionName = "DELETE";
+ this.entityName = "COLLATERAL_PRODUCT";
+ this.entityId = collateralId;
+ this.href = "/collateral-management/delete/" + collateralId;
+ return this;
+ }
+
+ public CommandWrapperBuilder deleteClientCollateralProduct(final Long
collateralId, final Long clientId) {
+ this.actionName = "DELETE";
+ this.entityName = "CLIENT_COLLATERAL_PRODUCT";
+ this.entityId = collateralId;
+ this.clientId = clientId;
+ this.href = "/collateral-management/" + collateralId + "/clients/" +
clientId;
+ return this;
Review comment:
Shouldn't this be consistent with the above ones... ie /clients/+
clientid + /collateral/+ collateralid
##########
File path:
fineract-provider/src/main/java/org/apache/fineract/portfolio/collateralmanagement/api/CollateralAPIConstants.java
##########
@@ -0,0 +1,46 @@
+/**
+ * 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.collateralmanagement.api;
+
+public final class CollateralAPIConstants {
+
Review comment:
Why wrap the enum in a class? Just have the enum - look at
AccountingRuleJsonInputParams as an example
##########
File path:
fineract-provider/src/main/java/org/apache/fineract/portfolio/collateralmanagement/api/CollateralManagementAPIResource.java
##########
@@ -0,0 +1,189 @@
+/**
+ * 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.collateralmanagement.api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.Collection;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+import org.apache.fineract.commands.domain.CommandWrapper;
+import org.apache.fineract.commands.service.CommandWrapperBuilder;
+import
org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
+import
org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
+import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import
org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.organisation.monetary.data.CurrencyData;
+import
org.apache.fineract.organisation.monetary.service.CurrencyReadPlatformService;
+import
org.apache.fineract.portfolio.collateralmanagement.api.CollateralAPIConstants.CollateralManagementJSONInputParams;
+import
org.apache.fineract.portfolio.collateralmanagement.data.CollateralManagementData;
+import
org.apache.fineract.portfolio.collateralmanagement.service.ClientCollateralManagementReadPlatformService;
+import
org.apache.fineract.portfolio.collateralmanagement.service.CollateralManagementReadPlatformService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+@Path("/collateral-management")
+@Component
+@Scope("singleton")
+@Tag(name = "Collateral Management", description = "Collateral Management is
for managing collateral operations")
+public class CollateralManagementAPIResource {
+
Review comment:
Should be CollateralManagementApiResource
##########
File path:
fineract-provider/src/main/java/org/apache/fineract/portfolio/collateralmanagement/domain/CollateralManagementDomain.java
##########
@@ -0,0 +1,154 @@
+/**
+ * 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.collateralmanagement.domain;
+
+import java.math.BigDecimal;
+import java.util.HashSet;
+import java.util.Set;
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import
org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
+import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
+import
org.apache.fineract.portfolio.collateralmanagement.api.CollateralAPIConstants;
+
+@Entity
+@Table(name = "m_collateral_management")
+public class CollateralManagementDomain extends AbstractPersistableCustom {
+
+ @Column(name = "name", length = 20, columnDefinition = " ")
+ private String name;
+
+ @Column(name = "quality", nullable = false, length = 40)
+ private String quality;
+
+ @Column(name = "base_price", nullable = false, scale = 5, precision = 20)
+ private BigDecimal basePrice;
+
+ @Column(name = "unit_type", nullable = false, length = 10)
+ private String unitType;
+
+ @Column(name = "pct_to_base", nullable = false, scale = 5, precision = 20)
+ private BigDecimal pctToBase;
+
+ @ManyToOne
+ @JoinColumn(name = "currency")
+ private ApplicationCurrency currency;
+
+ @OneToMany(mappedBy = "collateral", cascade = CascadeType.ALL,
orphanRemoval = true, fetch = FetchType.EAGER)
+ private Set<ClientCollateralManagement> clientCollateralManagements = new
HashSet<>();
+
+ public CollateralManagementDomain() {
+
+ }
+
+ private CollateralManagementDomain(final String quality, final BigDecimal
basePrice, final String unitType, final BigDecimal pctToBase,
+ final ApplicationCurrency currency, final String name) {
+ this.basePrice = basePrice;
+ this.currency = currency;
+ this.pctToBase = pctToBase;
+ this.unitType = unitType;
+ this.quality = quality;
+ this.name = name;
+ }
+
+ public static CollateralManagementDomain createNew(JsonCommand
jsonCommand, final ApplicationCurrency applicationCurrency) {
+ /**
+ * TODO: Add data validity errors.
+ */
+
Review comment:
Is this to-do still valid - validations to be added?
##########
File path:
fineract-provider/src/main/java/org/apache/fineract/portfolio/collateralmanagement/service/CollateralManagementWritePlatformServiceImpl.java
##########
@@ -0,0 +1,179 @@
+/**
+ * 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.collateralmanagement.service;
+
+import com.google.gson.JsonElement;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.ApiParameterError;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import
org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
+import
org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
+import
org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepository;
+import
org.apache.fineract.portfolio.collateralmanagement.api.CollateralAPIConstants;
+import
org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagement;
+import
org.apache.fineract.portfolio.collateralmanagement.domain.CollateralManagementDomain;
+import
org.apache.fineract.portfolio.collateralmanagement.domain.CollateralManagementRepositoryWrapper;
+import
org.apache.fineract.portfolio.collateralmanagement.exception.CollateralCannotBeDeletedException;
+import
org.apache.fineract.portfolio.collateralmanagement.exception.CollateralNotFoundException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+public class CollateralManagementWritePlatformServiceImpl implements
CollateralManagementWritePlatformService {
+
+ private final CollateralManagementRepositoryWrapper
collateralManagementRepositoryWrapper;
+ private final ApplicationCurrencyRepository applicationCurrencyRepository;
+ private final FromJsonHelper fromApiJsonHelper;
+
+ @Autowired
+ public CollateralManagementWritePlatformServiceImpl(final
CollateralManagementRepositoryWrapper collateralManagementRepositoryWrapper,
+ final ApplicationCurrencyRepository applicationCurrencyRepository,
final FromJsonHelper fromApiJsonHelper) {
+ this.collateralManagementRepositoryWrapper =
collateralManagementRepositoryWrapper;
+ this.applicationCurrencyRepository = applicationCurrencyRepository;
+ this.fromApiJsonHelper = fromApiJsonHelper;
+ }
+
+ @Transactional
+ @Override
+ public CommandProcessingResult createCollateral(final JsonCommand
jsonCommand) {
+
+ validateForCreation(jsonCommand);
+ final String currencyParamName = jsonCommand
+
.stringValueOfParameterNamed(CollateralAPIConstants.CollateralManagementJSONInputParams.CURRENCY.getValue());
+ final ApplicationCurrency applicationCurrency =
this.applicationCurrencyRepository.findOneByCode(currencyParamName);
+
+ final CollateralManagementDomain collateral =
CollateralManagementDomain.createNew(jsonCommand, applicationCurrency);
+ this.collateralManagementRepositoryWrapper.create(collateral);
+ return new
CommandProcessingResultBuilder().withCommandId(jsonCommand.commandId()).withEntityId(collateral.getId()).build();
+ }
+
+ private void validateForCreation(JsonCommand jsonCommand) {
+ final JsonElement jsonElement =
this.fromApiJsonHelper.parse(jsonCommand.json());
+ final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+ final DataValidatorBuilder baseDataValidator = new
DataValidatorBuilder(dataValidationErrors).resource("collateral-management");
+
+ if (!jsonCommand.parameterExists("locale")) {
+
baseDataValidator.reset().parameter("locale").notNull().failWithCode("locale.not.exists");
+ } else {
+ final String locale =
this.fromApiJsonHelper.extractStringNamed("locale", jsonElement);
+
baseDataValidator.reset().parameter("locale").value(locale).notNull();
+ }
+
+ if
(!jsonCommand.parameterExists(CollateralAPIConstants.CollateralManagementJSONInputParams.CURRENCY.getValue()))
{
+
baseDataValidator.reset().parameter(CollateralAPIConstants.CollateralManagementJSONInputParams.CURRENCY.getValue()).notNull()
+ .failWithCode("currency.not.exists");
+ } else {
+ final String currency =
this.fromApiJsonHelper.extractStringNamed("currency", jsonElement);
+
baseDataValidator.reset().parameter("currency").value(currency).notNull();
+ }
+
+ if (!jsonCommand.parameterExists("quality")) {
+
baseDataValidator.reset().parameter(CollateralAPIConstants.CollateralManagementJSONInputParams.QUALITY.getValue()).notNull()
+ .failWithCode("quality.not.exists");
+ } else {
+ final String quality =
this.fromApiJsonHelper.extractStringNamed("quality", jsonElement);
+
baseDataValidator.reset().parameter("quality").value(quality).notNull();
+ }
+
+ if (!jsonCommand.parameterExists("basePrice")) {
+
baseDataValidator.reset().parameter("basePrice").notNull().notBlank().failWithCode("basePrice.not.exists");
+ } else {
+ final BigDecimal basePrice =
this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("basePrice",
jsonElement);
+
baseDataValidator.reset().parameter("basePrice").value(basePrice).notNull().positiveAmount();
+ }
+
+ if (!jsonCommand.parameterExists("unitType")) {
+
baseDataValidator.reset().parameter("unitType").notNull().notBlank().failWithCode("unitType.not.exists");
+ } else {
+ final String unitType =
this.fromApiJsonHelper.extractStringNamed("unitType", jsonElement);
+
baseDataValidator.reset().parameter("unitType").value(unitType).notNull();
+ }
+
+ if (!jsonCommand.parameterExists("pctToBase")) {
+
baseDataValidator.reset().parameter("pctToBase").notNull().notBlank().failWithCode("pctToBase.not.exists");
+ } else {
+ final BigDecimal basePrice =
this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("pctToBase",
jsonElement);
+
baseDataValidator.reset().parameter("pctToBase").value(basePrice).notNull().positiveAmount();
+ }
+
+ if (!jsonCommand.parameterExists("name")) {
+
baseDataValidator.reset().parameter("name").notNull().notBlank().failWithCode("name.not.exists");
+ } else {
+ final String unitType =
this.fromApiJsonHelper.extractStringNamed("name", jsonElement);
+
baseDataValidator.reset().parameter("name").value(unitType).notNull();
+ }
+
+ if (!dataValidationErrors.isEmpty()) {
+ throw new PlatformApiDataValidationException(dataValidationErrors);
+ }
+ }
+
+ @Transactional
+ @Override
+ public CommandProcessingResult updateCollateral(final Long collateralId,
JsonCommand jsonCommand) {
+ final CollateralManagementDomain collateral =
this.collateralManagementRepositoryWrapper.getCollateral(collateralId);
+ final String currencyParamName =
CollateralAPIConstants.CollateralManagementJSONInputParams.CURRENCY.getValue();
+ final ApplicationCurrency applicationCurrency =
this.applicationCurrencyRepository
+
.findOneByCode(jsonCommand.stringValueOfParameterNamed(currencyParamName));
+ if (jsonCommand.isChangeInStringParameterNamed(currencyParamName,
applicationCurrency.getCode())) {
+ final String newValue =
jsonCommand.stringValueOfParameterNamed(currencyParamName);
+ applicationCurrency.setCode(newValue);
+ }
+ collateral.update(jsonCommand, applicationCurrency);
+ this.collateralManagementRepositoryWrapper.update(collateral);
+ /**
+ * TODO: Return changes.
+ */
+ return new
CommandProcessingResultBuilder().withCommandId(jsonCommand.commandId()).withEntityId(jsonCommand.entityId()).build();
Review comment:
Todo still valid?
##########
File path:
fineract-provider/src/main/java/org/apache/fineract/portfolio/collateralmanagement/domain/ClientCollateralManagementRepositoryWrapper.java
##########
@@ -0,0 +1,107 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.collateralmanagement.domain;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.fineract.portfolio.client.domain.Client;
+import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
+import
org.apache.fineract.portfolio.collateralmanagement.data.ClientCollateralManagementData;
+import
org.apache.fineract.portfolio.collateralmanagement.exception.ClientCollateralNotFoundException;
+import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
+import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
+import
org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ClientCollateralManagementRepositoryWrapper {
+
+ private final ClientCollateralManagementRepository
clientCollateralManagementRepository;
+ private final ClientRepositoryWrapper clientRepositoryWrapper;
+ private final LoanProductRepository loanProductRepository;
+
+ @Autowired
+ public ClientCollateralManagementRepositoryWrapper(final
ClientCollateralManagementRepository clientCollateralManagementRepository,
+ final ClientRepositoryWrapper clientRepositoryWrapper, final
LoanProductRepository loanProductRepository) {
+ this.clientCollateralManagementRepository =
clientCollateralManagementRepository;
+ this.clientRepositoryWrapper = clientRepositoryWrapper;
+ this.loanProductRepository = loanProductRepository;
+ }
+
+ public List<ClientCollateralManagement> getCollateralsPerClient(final Long
clientId) {
+ final Client client =
this.clientRepositoryWrapper.findOneWithNotFoundDetection(clientId);
+ return
this.clientCollateralManagementRepository.findByClientId(client);
+ }
+
+ public List<ClientCollateralManagementData> getClientCollateralData(final
Long clientId, final Long prodId) {
+ final Client client =
this.clientRepositoryWrapper.findOneWithNotFoundDetection(clientId);
+ String currency = null;
+ if (prodId != null) {
+ final LoanProduct loanProduct =
this.loanProductRepository.findById(prodId)
+ .orElseThrow(() -> new
LoanProductNotFoundException(prodId));
+ currency = loanProduct.getCurrency().getCode();
+ }
+ List<ClientCollateralManagement> clientCollateralManagements =
this.clientCollateralManagementRepository.findByClientId(client);
+ List<ClientCollateralManagementData> clientCollateralManagementDataSet
= new ArrayList<>();
+ for (ClientCollateralManagement clientCollateralManagement :
clientCollateralManagements) {
+ BigDecimal quantity = clientCollateralManagement.getQuantity();
+ BigDecimal basePrice = null;
+ BigDecimal pctToBase = null;
+ if (BigDecimal.ZERO.compareTo(quantity) == 0) {
+ basePrice = BigDecimal.ZERO;
+ pctToBase = BigDecimal.ZERO;
+ } else {
+ basePrice =
clientCollateralManagement.getCollaterals().getBasePrice();
+ pctToBase =
clientCollateralManagement.getCollaterals().getPctToBase().divide(BigDecimal.valueOf(100));
+ }
+ BigDecimal total = quantity.multiply(basePrice);
+ BigDecimal totalCollateralValue = total.multiply(pctToBase);
Review comment:
Would it make sense to move all this logic (the previous 10 lines or so)
to the ClientCollateralManagementData class?
##########
File path:
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java
##########
@@ -313,12 +322,28 @@ public ClientData retrieveOne(final Long clientId) {
final ClientData clientData =
this.jdbcTemplate.queryForObject(sql, this.clientMapper,
new Object[] { hierarchySearchString,
hierarchySearchString, clientId });
+ // Get client collaterals
+ final Collection<ClientCollateralManagement>
clientCollateralManagements = this.clientCollateralManagementRepositoryWrapper
+ .getCollateralsPerClient(clientId);
+ final Set<ClientCollateralManagementData>
clientCollateralManagementDataSet = new HashSet<>();
+
+ // Map to client collateral data class
+ for (ClientCollateralManagement clientCollateralManagement :
clientCollateralManagements) {
+ BigDecimal pctToBase =
clientCollateralManagement.getCollaterals().getPctToBase().divide(BigDecimal.valueOf(100));
+ BigDecimal basePrice =
clientCollateralManagement.getCollaterals().getBasePrice();
+ BigDecimal quantity = clientCollateralManagement.getQuantity();
+ BigDecimal total = basePrice.multiply(quantity);
+ BigDecimal totalCollateral = total.multiply(pctToBase);
+ clientCollateralManagementDataSet
Review comment:
If this logic of calculating total and totalCollateral is always the
same, why don't we calculate this within the ClientCollateralManagementData
class rather than here?
##########
File path:
fineract-provider/src/main/java/org/apache/fineract/portfolio/collateralmanagement/api/CollateralAPIConstants.java
##########
@@ -0,0 +1,46 @@
+/**
+ * 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.collateralmanagement.api;
+
+public final class CollateralAPIConstants {
+
+ public enum CollateralManagementJSONInputParams {
+
Review comment:
Naming convention - should be Json not JSON
##########
File path:
fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
##########
@@ -1814,6 +1822,32 @@ public CommandWrapperBuilder updateCollateral(final Long
loanId, final Long coll
return this;
}
+ public CommandWrapperBuilder updateCollateralProduct(final Long
collateralId) {
+ this.actionName = "UPDATE";
+ this.entityName = "COLLATERAL_PRODUCT";
+ this.entityId = collateralId;
+ this.href = "/collateral-management/" + collateralId;
+ return this;
+ }
+
+ public CommandWrapperBuilder updateClientCollateralProduct(final Long
clientId, final Long collateralId) {
+ this.actionName = "UPDATE";
+ this.entityName = "CLIENT_COLLATERAL_PRODUCT";
+ this.entityId = collateralId;
+ this.clientId = clientId;
+ this.href = "/clients/" + clientId + "/collateral" + collateralId;
+ return this;
+ }
+
+ public CommandWrapperBuilder deleteLoanCollateral(final Long loanId, final
Long collateralId) {
+ this.actionName = "DELETE";
+ this.entityName = "LOAN_COLLATERAL_PRODUCT";
+ this.entityId = collateralId;
+ this.loanId = loanId;
+ this.href = "/loans/" + loanId + "/collateral" + collateralId;
+ return this;
Review comment:
Same as above
##########
File path:
fineract-provider/src/main/java/org/apache/fineract/portfolio/collateralmanagement/api/ClientCollateralManagementAPIResource.java
##########
@@ -0,0 +1,203 @@
+/**
+ * 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.collateralmanagement.api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+import org.apache.fineract.commands.domain.CommandWrapper;
+import org.apache.fineract.commands.service.CommandWrapperBuilder;
+import
org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
+import
org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
+import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import
org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import
org.apache.fineract.portfolio.collateralmanagement.data.ClientCollateralManagementData;
+import
org.apache.fineract.portfolio.collateralmanagement.data.LoanCollateralTemplateData;
+import
org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagement;
+import
org.apache.fineract.portfolio.collateralmanagement.service.ClientCollateralManagementReadPlatformService;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+@Path("/clients/{clientId}/collaterals")
+@Component
+@Scope("singleton")
+@Tag(name = "Client Collateral Management", description = "Client Collateral
Management is for managing collateral operations")
+public class ClientCollateralManagementAPIResource {
+
Review comment:
Naming convention - should be ManagementApiResource not
ManagementAPIResource
##########
File path:
fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/importhandler/loan/LoanImportHandler.java
##########
@@ -295,6 +300,21 @@ private LoanAccountData readLoan(Row row, String locale,
String dateFormat) {
ImportHandlerUtils.readAsDate(LoanConstants.CHARGE_DUE_DATE_2, row), null));
}
}
+
+ List<LoanCollateralManagementData> loanCollateralManagementData = new
ArrayList<>();
+
+ if (collateralId != null) {
+ if
(ImportHandlerUtils.readAsDouble(LoanConstants.LOAN_COLLATERAL_QUANTITY, row)
!= null) {
+ loanCollateralManagementData.add(new
LoanCollateralManagementData(collateralId,
+
BigDecimal.valueOf(ImportHandlerUtils.readAsDouble(LoanConstants.LOAN_COLLATERAL_QUANTITY,
row)), null, null,
+ null));
+ } else {
+ throw new InvalidAmountOfCollateralQuantity(null);
+ }
+ } else {
+ throw new PlatformDataIntegrityException("404",
"err.parameter.collateral.not.found");
+ }
Review comment:
This doesn't seem right. The code at the end of this function says that
loanCollateralManagementData is not needed for all types of loans - yet here we
throw an exception if collateralId == null. What about those loans that don't
need collateral (e.g. loanType.equals("jlg") below)?
##########
File path:
fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
##########
@@ -1823,6 +1857,31 @@ public CommandWrapperBuilder deleteCollateral(final Long
loanId, final Long coll
return this;
}
+ public CommandWrapperBuilder deleteCollateralProduct(final Long
collateralId) {
+ this.actionName = "DELETE";
+ this.entityName = "COLLATERAL_PRODUCT";
+ this.entityId = collateralId;
+ this.href = "/collateral-management/delete/" + collateralId;
+ return this;
Review comment:
Do we need the /delete/ in the href... is it not clear from the action
that this is a delete?
##########
File path:
fineract-provider/src/main/java/org/apache/fineract/portfolio/collateralmanagement/api/ClientCollateralManagementAPIResource.java
##########
@@ -0,0 +1,203 @@
+/**
+ * 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.collateralmanagement.api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+import org.apache.fineract.commands.domain.CommandWrapper;
+import org.apache.fineract.commands.service.CommandWrapperBuilder;
+import
org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
+import
org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
+import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import
org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import
org.apache.fineract.portfolio.collateralmanagement.data.ClientCollateralManagementData;
+import
org.apache.fineract.portfolio.collateralmanagement.data.LoanCollateralTemplateData;
+import
org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagement;
+import
org.apache.fineract.portfolio.collateralmanagement.service.ClientCollateralManagementReadPlatformService;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+@Path("/clients/{clientId}/collaterals")
+@Component
+@Scope("singleton")
+@Tag(name = "Client Collateral Management", description = "Client Collateral
Management is for managing collateral operations")
+public class ClientCollateralManagementAPIResource {
+
+ private final DefaultToApiJsonSerializer<ClientCollateralManagement>
apiJsonSerializerService;
+ private final DefaultToApiJsonSerializer<ClientCollateralManagementData>
apiJsonSerializerDataService;
+ private final DefaultToApiJsonSerializer<LoanCollateralTemplateData>
apiJsonSerializerForLoanCollateralTemplateService;
+ private final ApiRequestParameterHelper apiRequestParameterHelper;
+ private final PortfolioCommandSourceWritePlatformService
commandsSourceWritePlatformService;
+ private final PlatformSecurityContext context;
+ private final CodeValueReadPlatformService codeValueReadPlatformService;
+ private final ClientCollateralManagementReadPlatformService
clientCollateralManagementReadPlatformService;
+ private static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(
+ Arrays.asList("name", "quantity", "total", "totalCollateral",
"clientId", "loanTransactionData"));
+
+ public ClientCollateralManagementAPIResource(final
DefaultToApiJsonSerializer<ClientCollateralManagement> apiJsonSerializerService,
+ final ApiRequestParameterHelper apiRequestParameterHelper,
+ final PortfolioCommandSourceWritePlatformService
commandsSourceWritePlatformService, final PlatformSecurityContext context,
+ final CodeValueReadPlatformService codeValueReadPlatformService,
+ final ClientCollateralManagementReadPlatformService
clientCollateralManagementReadPlatformService,
+ final DefaultToApiJsonSerializer<ClientCollateralManagementData>
apiJsonSerializerDataService,
+ final DefaultToApiJsonSerializer<LoanCollateralTemplateData>
apiJsonSerializerForLoanCollateralTemplateService) {
+ this.apiJsonSerializerService = apiJsonSerializerService;
+ this.apiRequestParameterHelper = apiRequestParameterHelper;
+ this.commandsSourceWritePlatformService =
commandsSourceWritePlatformService;
+ this.context = context;
+ this.codeValueReadPlatformService = codeValueReadPlatformService;
+ this.clientCollateralManagementReadPlatformService =
clientCollateralManagementReadPlatformService;
+ this.apiJsonSerializerDataService = apiJsonSerializerDataService;
+ this.apiJsonSerializerForLoanCollateralTemplateService =
apiJsonSerializerForLoanCollateralTemplateService;
+ }
+
+ @GET
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Get Clients Collateral Products", description = "Get
Collateral Product of a Client")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(array = @ArraySchema(schema = @Schema(implementation =
ClientCollateralManagementAPIResourceSwagger.GetClientCollateralManagementsResponse.class))))
})
+ public String getClientCollateral(@PathParam("clientId")
@Parameter(description = "clientId") final Long clientId,
+ @Context final UriInfo uriInfo, @QueryParam("prodId")
@Parameter(description = "prodId") final Long prodId) {
+
+ this.context.authenticatedUser().validateHasReadPermission(
+
CollateralAPIConstants.CollateralManagementJSONInputParams.CLIENT_COLLATERAL_PRODUCT_READ_PERMISSION.getValue());
+
+ List<ClientCollateralManagementData> collateralProductList = null;
+
+ if (prodId != null) {
+ collateralProductList =
this.clientCollateralManagementReadPlatformService.getClientCollaterals(clientId,
prodId);
+ } else {
+ collateralProductList =
this.clientCollateralManagementReadPlatformService.getClientCollaterals(clientId,
null);
+ }
Review comment:
Why do we need this if... why not just pass the prodId as second
parameter? If it's null then you are passing null
##########
File path:
fineract-provider/src/main/java/org/apache/fineract/portfolio/collateralmanagement/api/CollateralManagementAPIResourceSwagger.java
##########
@@ -0,0 +1,159 @@
+/**
+ * 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.collateralmanagement.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.math.BigDecimal;
+
+final class CollateralManagementAPIResourceSwagger {
+
Review comment:
Api not API
##########
File path:
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
##########
@@ -524,6 +545,20 @@ public void recalculateAccruals(Loan loan, boolean
isInterestCalcualtionHappened
throw new
GeneralPlatformDomainRuleException(globalisationMessageCode, e.getMessage(), e);
}
}
+
+ // Update loan transaction on repayment.
Review comment:
Is this code in the right function? It's currently in
recalculateAccruals, which is called for many reasons e.g. when making charge
payments. Shouldn't the collateral be released only once the loan is paid off?
##########
File path:
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java
##########
@@ -847,38 +871,67 @@ public void validateForModify(final String json, final
LoanProduct loanProduct,
}
}
- // collateral
- final String collateralParameterName = "collateral";
- if (element.isJsonObject() &&
this.fromApiJsonHelper.parameterExists(collateralParameterName, element)) {
- final JsonObject topLevelJsonElement = element.getAsJsonObject();
- final Locale locale =
this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
- if (topLevelJsonElement.get("collateral").isJsonArray()) {
-
- final Type collateralParameterTypeOfMap = new
TypeToken<Map<String, Object>>() {}.getType();
- final Set<String> supportedParameters = new
HashSet<>(Arrays.asList("id", "type", "value", "description"));
- final JsonArray array =
topLevelJsonElement.get("collateral").getAsJsonArray();
- for (int i = 1; i <= array.size(); i++) {
- final JsonObject collateralItemElement = array.get(i -
1).getAsJsonObject();
-
- final String collateralJson =
this.fromApiJsonHelper.toJson(collateralItemElement);
-
this.fromApiJsonHelper.checkForUnsupportedParameters(collateralParameterTypeOfMap,
collateralJson, supportedParameters);
-
- final Long collateralTypeId =
this.fromApiJsonHelper.extractLongNamed("type", collateralItemElement);
-
baseDataValidator.reset().parameter("collateral").parameterAtIndexArray("type",
i).value(collateralTypeId).notNull()
- .integerGreaterThanZero();
-
- final BigDecimal collateralValue =
this.fromApiJsonHelper.extractBigDecimalNamed("value", collateralItemElement,
- locale);
-
baseDataValidator.reset().parameter("collateral").parameterAtIndexArray("value",
i).value(collateralValue)
- .ignoreIfNull().positiveAmount();
+ final String loanTypeParameterName = "loanType";
+ final String loanTypeStr =
this.fromApiJsonHelper.extractStringNamed(loanTypeParameterName, element);
+
baseDataValidator.reset().parameter(loanTypeParameterName).value(loanTypeStr).notNull();
- final String description =
this.fromApiJsonHelper.extractStringNamed("description", collateralItemElement);
-
baseDataValidator.reset().parameter("collateral").parameterAtIndexArray("description",
i).value(description).notBlank()
- .notExceedingLengthOf(500);
+ if (!StringUtils.isBlank(loanTypeStr)) {
+ final AccountType loanType = AccountType.fromName(loanTypeStr);
+
baseDataValidator.reset().parameter(loanTypeParameterName).value(loanType.getValue()).inMinMaxRange(1,
4);
Review comment:
I don't think we should be comparing against hardcoded range... you can
use e.g. AccountNumerations.loanType() and check if the result is INVALID...
or baseDataValidator.isOneOfEnumValues()
##########
File path:
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java
##########
@@ -627,6 +636,29 @@ private void checkForProductMixRestrictions(final
List<Long> activeLoansLoanProd
}
}
+ // private void updateLoanCollateral(Set<LoanCollateralManagement>
loanCollateralManagementSet) {
+ //
this.loanCollateralManagementRepository.saveAll(loanCollateralManagementSet);
+ // }
+ //
+ // private void updateClientCollaterals(Set<ClientCollateralManagement>
clientCollateralManagementSet) {
+ //
this.clientCollateralManagementRepository.saveAll(clientCollateralManagementSet);
+ // }
+ //
+ // private boolean isLoanCollateralUpdatable(JsonCommand command) {
+ // boolean isExist = this.fromJsonHelper.parameterExists("collaterals",
command.parsedJson());
+ // if (!isExist) {
+ // return false;
+ // }
+ //
+ // int length = this.fromJsonHelper.extractJsonArrayNamed("collaterals",
command.parsedJson()).size();
+ //
+ // if (length == 0) {
+ // return false;
+ // }
Review comment:
Should this code be commented out?
##########
File path:
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
##########
@@ -494,10 +498,14 @@ private Loan(final String accountNo, final Client client,
final Group group, fin
this.charges = null;
this.summary = new LoanSummary();
}
- if (collateral != null && !collateral.isEmpty()) {
- this.collateral = associateWithThisLoan(collateral);
+
+ /**
+ * TODO: Apply for other loan type if required.
+ */
+ if (loanType.equals(1) && collateral != null && !collateral.isEmpty())
{
Review comment:
Todo still valid?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]