You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ra...@apache.org on 2016/04/13 16:35:15 UTC
[03/12] incubator-fineract git commit: Shares And Dividends
Implementation
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/08c553f9/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/serialization/ShareProductDataSerializer.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/serialization/ShareProductDataSerializer.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/serialization/ShareProductDataSerializer.java
new file mode 100644
index 0000000..345294b
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/serialization/ShareProductDataSerializer.java
@@ -0,0 +1,460 @@
+/**
+ * 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.shareproducts.serialization;
+
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.fineract.accounting.common.AccountingRuleType;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.ApiParameterError;
+import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
+import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
+import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
+import org.apache.fineract.portfolio.charge.domain.Charge;
+import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
+import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
+import org.apache.fineract.portfolio.loanproduct.exception.InvalidCurrencyException;
+import org.apache.fineract.portfolio.shareproducts.constants.ShareProductApiConstants;
+import org.apache.fineract.portfolio.shareproducts.data.ShareProductMarketPriceData;
+import org.apache.fineract.portfolio.shareproducts.domain.ShareProduct;
+import org.apache.fineract.portfolio.shareproducts.domain.ShareProductMarketPrice;
+import org.apache.fineract.useradministration.domain.AppUser;
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.reflect.TypeToken;
+
+@Service
+public class ShareProductDataSerializer {
+
+ private final FromJsonHelper fromApiJsonHelper;
+
+ private final ChargeRepositoryWrapper chargeRepository;
+
+ private final PlatformSecurityContext platformSecurityContext;
+
+ @Autowired
+ public ShareProductDataSerializer(final FromJsonHelper fromApiJsonHelper, final ChargeRepositoryWrapper chargeRepository,
+ final PlatformSecurityContext platformSecurityContext) {
+ this.fromApiJsonHelper = fromApiJsonHelper;
+ this.chargeRepository = chargeRepository;
+ this.platformSecurityContext = platformSecurityContext;
+ }
+
+ public ShareProduct validateAndCreate(JsonCommand jsonCommand) {
+ if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); }
+ final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
+ this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(),
+ ShareProductApiConstants.supportedParametersForCreate);
+
+ final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+ final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("sharesproduct");
+ JsonElement element = jsonCommand.parsedJson();
+ final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject());
+ final String productName = this.fromApiJsonHelper.extractStringNamed(ShareProductApiConstants.name_paramname, element);
+ baseDataValidator.reset().parameter(ShareProductApiConstants.name_paramname).value(productName).notBlank()
+ .notExceedingLengthOf(200);
+ final String shortName = this.fromApiJsonHelper.extractStringNamed(ShareProductApiConstants.shortname_paramname, element);
+ baseDataValidator.reset().parameter(ShareProductApiConstants.shortname_paramname).value(shortName).notBlank()
+ .notExceedingLengthOf(4);
+ String description = null;
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.description_paramname, element)) {
+ description = this.fromApiJsonHelper.extractStringNamed(ShareProductApiConstants.description_paramname, element);
+ }
+
+ String externalId = this.fromApiJsonHelper.extractStringNamed(ShareProductApiConstants.externalid_paramname, element);
+ // baseDataValidator.reset().parameter(ShareProductApiConstants.externalid_paramname).value(externalId).notBlank();
+
+ Long totalNumberOfShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.totalshares_paramname, element);
+ baseDataValidator.reset().parameter(ShareProductApiConstants.totalshares_paramname).value(totalNumberOfShares).notNull()
+ .longGreaterThanZero();
+
+ final String currencyCode = this.fromApiJsonHelper.extractStringNamed(ShareProductApiConstants.currency_paramname, element);
+ final Integer digitsAfterDecimal = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(
+ ShareProductApiConstants.digitsafterdecimal_paramname, element);
+ final Integer inMultiplesOf = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(
+ ShareProductApiConstants.inmultiplesof_paramname, element);
+ final MonetaryCurrency currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal, inMultiplesOf);
+ final Long sharesIssued = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.totalsharesissued_paramname, element);
+ final BigDecimal unitPrice = this.fromApiJsonHelper.extractBigDecimalNamed(ShareProductApiConstants.unitprice_paramname, element,
+ locale);
+ baseDataValidator.reset().parameter(ShareProductApiConstants.unitprice_paramname).value(unitPrice).notNull().positiveAmount();
+
+ BigDecimal shareCapitalValue = BigDecimal.ONE;
+ if (sharesIssued != null && unitPrice != null) {
+ shareCapitalValue = BigDecimal.valueOf(sharesIssued).multiply(unitPrice);
+ }
+
+ Integer accountingRule = this.fromApiJsonHelper.extractIntegerNamed(ShareProductApiConstants.accountingRuleParamName, element,
+ locale);
+ baseDataValidator.reset().parameter(ShareProductApiConstants.accountingRuleParamName).value(accountingRule).notNull()
+ .integerGreaterThanZero();
+ AccountingRuleType accountingRuleType = null;
+ if (accountingRule != null) {
+ accountingRuleType = AccountingRuleType.fromInt(accountingRule);
+ }
+
+ Long minimumClientShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.minimumshares_paramname, element);
+ Long nominalClientShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.nominaltshares_paramname, element);
+ baseDataValidator.reset().parameter(ShareProductApiConstants.nominaltshares_paramname).value(nominalClientShares).notNull()
+ .longGreaterThanZero();
+ if (minimumClientShares != null && nominalClientShares != null) {
+ baseDataValidator.reset().parameter(ShareProductApiConstants.nominaltshares_paramname).value(nominalClientShares)
+ .longGreaterThanNumber(minimumClientShares);
+ }
+ Long maximumClientShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.maximumshares_paramname, element);
+ if (maximumClientShares != null && nominalClientShares != null) {
+ baseDataValidator.reset().parameter(ShareProductApiConstants.maximumshares_paramname).value(maximumClientShares)
+ .longGreaterThanNumber(nominalClientShares);
+ }
+
+ Set<ShareProductMarketPrice> marketPriceSet = asembleShareMarketPrice(element);
+ Set<Charge> charges = assembleListOfProductCharges(element, currencyCode);
+ Boolean allowdividendsForInactiveClients = this.fromApiJsonHelper.extractBooleanNamed(
+ ShareProductApiConstants.allowdividendcalculationforinactiveclients_paramname, element);
+
+ Integer minimumActivePeriod = this.fromApiJsonHelper.extractIntegerNamed(
+ ShareProductApiConstants.minimumactiveperiodfordividends_paramname, element, locale);
+ PeriodFrequencyType minimumActivePeriodType = extractPeriodType(
+ ShareProductApiConstants.minimumactiveperiodfrequencytype_paramname, element);
+ if (minimumActivePeriod != null) {
+ baseDataValidator.reset().parameter(ShareProductApiConstants.minimumactiveperiodfrequencytype_paramname)
+ .value(minimumActivePeriodType.getValue()).integerSameAsNumber(PeriodFrequencyType.DAYS.getValue());
+ }
+
+ Integer lockinPeriod = this.fromApiJsonHelper.extractIntegerNamed(ShareProductApiConstants.lockperiod_paramname, element, locale);
+ PeriodFrequencyType lockPeriodType = extractPeriodType(ShareProductApiConstants.lockinperiodfrequencytype_paramname, element);
+
+ AppUser createdBy = platformSecurityContext.authenticatedUser();
+ AppUser modifiedBy = createdBy;
+ DateTime createdDate = DateUtils.getLocalDateTimeOfTenant().toDateTime();
+ DateTime modifiedOn = createdDate;
+ ShareProduct product = new ShareProduct(productName, shortName, description, externalId, currency, totalNumberOfShares,
+ sharesIssued, unitPrice, shareCapitalValue, minimumClientShares, nominalClientShares, maximumClientShares, marketPriceSet,
+ charges, allowdividendsForInactiveClients, lockinPeriod, lockPeriodType, minimumActivePeriod, minimumActivePeriodType,
+ createdBy, createdDate, modifiedBy, modifiedOn, accountingRuleType);
+ for (ShareProductMarketPrice data : marketPriceSet) {
+ data.setShareProduct(product);
+ }
+ if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
+ return product;
+ }
+
+ private PeriodFrequencyType extractPeriodType(String paramName, final JsonElement element) {
+ PeriodFrequencyType frequencyType = PeriodFrequencyType.INVALID;
+ frequencyType = PeriodFrequencyType.fromInt(this.fromApiJsonHelper.extractIntegerWithLocaleNamed(paramName, element));
+ return frequencyType;
+ }
+
+ private Set<ShareProductMarketPriceData> asembleShareMarketPriceForUpdate(final JsonElement element) {
+ Set<ShareProductMarketPriceData> set = null;
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.marketprice_paramname, element)) {
+ set = new HashSet<>();
+ JsonArray array = this.fromApiJsonHelper.extractJsonArrayNamed(ShareProductApiConstants.marketprice_paramname, element);
+ for (JsonElement arrayElement : array) {
+ Long id = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.id_paramname, arrayElement);
+ LocalDate localDate = this.fromApiJsonHelper.extractLocalDateNamed(ShareProductApiConstants.startdate_paramname,
+ arrayElement);
+ final BigDecimal shareValue = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
+ ShareProductApiConstants.sharevalue_paramname, arrayElement);
+ ShareProductMarketPriceData obj = new ShareProductMarketPriceData(id, localDate.toDate(), shareValue);
+ set.add(obj);
+ }
+ }
+ return set;
+ }
+
+ private Set<ShareProductMarketPrice> asembleShareMarketPrice(final JsonElement element) {
+ Set<ShareProductMarketPrice> set = null;
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.marketprice_paramname, element)) {
+ set = new HashSet<>();
+ JsonArray array = this.fromApiJsonHelper.extractJsonArrayNamed(ShareProductApiConstants.marketprice_paramname, element);
+ for (JsonElement arrayElement : array) {
+ LocalDate localDate = this.fromApiJsonHelper.extractLocalDateNamed(ShareProductApiConstants.startdate_paramname,
+ arrayElement);
+ final BigDecimal shareValue = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
+ ShareProductApiConstants.sharevalue_paramname, arrayElement);
+ ShareProductMarketPrice obj = new ShareProductMarketPrice(localDate.toDate(), shareValue);
+ set.add(obj);
+ }
+ }
+ return set;
+ }
+
+ private Set<Charge> assembleListOfProductCharges(final JsonElement element, final String currencyCode) {
+ final Set<Charge> charges = new HashSet<>();
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.charges_paramname, element)) {
+ JsonArray chargesArray = this.fromApiJsonHelper.extractJsonArrayNamed(ShareProductApiConstants.charges_paramname, element);
+ if (chargesArray != null) {
+ for (int i = 0; i < chargesArray.size(); i++) {
+ final JsonObject jsonObject = chargesArray.get(i).getAsJsonObject();
+ if (jsonObject.has("id")) {
+ final Long id = jsonObject.get("id").getAsLong();
+ final Charge charge = this.chargeRepository.findOneWithNotFoundDetection(id);
+ if (!currencyCode.equals(charge.getCurrencyCode())) {
+ final String errorMessage = "Charge and Share Product must have the same currency.";
+ throw new InvalidCurrencyException("charge", "attach.to.share.product", errorMessage);
+ }
+ charges.add(charge);
+ }
+ }
+ }
+ }
+ return charges;
+ }
+
+ public Map<String, Object> validateAndUpdate(JsonCommand jsonCommand, ShareProduct product) {
+ Map<String, Object> actualChanges = new HashMap<>();
+
+ if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); }
+ final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
+ this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(),
+ ShareProductApiConstants.supportedParametersForCreate);
+
+ final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+ final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("sharesproduct");
+
+ JsonElement element = jsonCommand.parsedJson();
+ final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject());
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.name_paramname, element)) {
+ final String productName = this.fromApiJsonHelper.extractStringNamed(ShareProductApiConstants.name_paramname, element);
+ baseDataValidator.reset().parameter(ShareProductApiConstants.name_paramname).value(productName).notBlank();
+ if (product.setProductName(productName)) {
+ actualChanges.put(ShareProductApiConstants.name_paramname, productName);
+ }
+ }
+
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.shortname_paramname, element)) {
+ final String shortName = this.fromApiJsonHelper.extractStringNamed(ShareProductApiConstants.shortname_paramname, element);
+ baseDataValidator.reset().parameter(ShareProductApiConstants.shortname_paramname).value(shortName).notBlank();
+ if (product.setShortName(shortName)) {
+ actualChanges.put(ShareProductApiConstants.shortname_paramname, shortName);
+ }
+ }
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.description_paramname, element)) {
+ String description = this.fromApiJsonHelper.extractStringNamed(ShareProductApiConstants.description_paramname, element);
+ if (product.setDescription(description)) {
+ actualChanges.put(ShareProductApiConstants.description_paramname, description);
+ }
+ }
+
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.externalid_paramname, element)) {
+ String externalId = this.fromApiJsonHelper.extractStringNamed(ShareProductApiConstants.externalid_paramname, element);
+ baseDataValidator.reset().parameter(ShareProductApiConstants.externalid_paramname).value(externalId).notBlank();
+ if (product.setExternalId(externalId)) {
+ actualChanges.put(ShareProductApiConstants.externalid_paramname, externalId);
+ }
+ }
+
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.totalshares_paramname, element)) {
+ Long totalShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.totalshares_paramname, element);
+ baseDataValidator.reset().parameter(ShareProductApiConstants.totalshares_paramname).value(totalShares).notNull()
+ .longGreaterThanZero();
+ if (product.setTotalShares(totalShares)) {
+ actualChanges.put(ShareProductApiConstants.totalshares_paramname, totalShares);
+ }
+ }
+ Long sharesIssued = null;
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.totalsharesissued_paramname, element)) {
+ sharesIssued = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.totalsharesissued_paramname, element);
+ if (product.setTotalIssuedShares(sharesIssued)) {
+ actualChanges.put(ShareProductApiConstants.totalsharesissued_paramname, sharesIssued);
+ }
+ }
+
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.currency_paramname, element)
+ && this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.digitsafterdecimal_paramname, element)
+ && this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.inmultiplesof_paramname, element)) {
+ final String currencyCode = this.fromApiJsonHelper.extractStringNamed(ShareProductApiConstants.currency_paramname, element);
+ final Integer digitsAfterDecimal = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(
+ ShareProductApiConstants.digitsafterdecimal_paramname, element);
+ final Integer inMultiplesOf = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(
+ ShareProductApiConstants.inmultiplesof_paramname, element);
+ final MonetaryCurrency currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal, inMultiplesOf);
+ if (product.setMonetaryCurrency(currency)) {
+ actualChanges.put(ShareProductApiConstants.currency_paramname, currency);
+ }
+ }
+
+ BigDecimal unitPrice = null;
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.unitprice_paramname, element)) {
+ unitPrice = this.fromApiJsonHelper.extractBigDecimalNamed(ShareProductApiConstants.unitprice_paramname, element, locale);
+ baseDataValidator.reset().parameter(ShareProductApiConstants.unitprice_paramname).value(unitPrice).notNull().positiveAmount();
+ if (product.setUnitPrice(unitPrice)) {
+ actualChanges.put(ShareProductApiConstants.unitprice_paramname, unitPrice);
+ }
+ }
+
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.accountingRuleParamName, element)) {
+ Integer accountingRule = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(ShareProductApiConstants.accountingRuleParamName,
+ element);
+ baseDataValidator.reset().parameter(ShareProductApiConstants.accountingRuleParamName).value(accountingRule).notNull()
+ .integerGreaterThanZero();
+ if (product.setAccountingRule(accountingRule)) {
+ actualChanges.put(ShareProductApiConstants.accountingRuleParamName, accountingRule);
+ }
+ }
+
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.minimumshares_paramname, element)) {
+ Long minimumClientShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.minimumshares_paramname, element);
+ baseDataValidator.reset().parameter(ShareProductApiConstants.minimumshares_paramname).value(minimumClientShares).notNull()
+ .longGreaterThanZero();
+ if (product.setMinimumShares(minimumClientShares)) {
+ actualChanges.put(ShareProductApiConstants.minimumshares_paramname, minimumClientShares);
+ }
+ }
+
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.nominaltshares_paramname, element)) {
+ Long nominalClientShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.nominaltshares_paramname, element);
+ baseDataValidator.reset().parameter(ShareProductApiConstants.nominaltshares_paramname).value(nominalClientShares).notNull()
+ .longGreaterThanZero();
+ if (product.setNominalShares(nominalClientShares)) {
+ actualChanges.put(ShareProductApiConstants.nominaltshares_paramname, nominalClientShares);
+ }
+ }
+
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.maximumshares_paramname, element)) {
+ Long maximumClientShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.maximumshares_paramname, element);
+ baseDataValidator.reset().parameter(ShareProductApiConstants.maximumshares_paramname).value(maximumClientShares).notNull()
+ .longGreaterThanZero();
+ if (product.setMaximumShares(maximumClientShares)) {
+ actualChanges.put(ShareProductApiConstants.maximumshares_paramname, maximumClientShares);
+ }
+ }
+
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.marketprice_paramname, element)) {
+ Set<ShareProductMarketPriceData> marketPrice = asembleShareMarketPriceForUpdate(element);
+ if (product.setMarketPrice(marketPrice)) {
+ actualChanges.put(ShareProductApiConstants.marketprice_paramname, marketPrice);
+ }
+ }
+
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.charges_paramname, element)) {
+ final String currencyCode = this.fromApiJsonHelper.extractStringNamed(ShareProductApiConstants.currency_paramname, element);
+ Set<Charge> charges = assembleListOfProductCharges(element, currencyCode);
+ if (product.setCharges(charges)) {
+ actualChanges.put(ShareProductApiConstants.charges_paramname, charges);
+ }
+ }
+
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.allowdividendcalculationforinactiveclients_paramname, element)) {
+ Boolean allowdividendsForInactiveClients = this.fromApiJsonHelper.extractBooleanNamed(
+ ShareProductApiConstants.allowdividendcalculationforinactiveclients_paramname, element);
+ if (product.setAllowDividendCalculationForInactiveClients(allowdividendsForInactiveClients)) {
+ actualChanges.put(ShareProductApiConstants.allowdividendcalculationforinactiveclients_paramname,
+ allowdividendsForInactiveClients);
+ }
+ }
+
+ Integer minimumActivePeriod = null;
+
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.minimumactiveperiodfordividends_paramname, element)) {
+ minimumActivePeriod = this.fromApiJsonHelper.extractIntegerNamed(
+ ShareProductApiConstants.minimumactiveperiodfordividends_paramname, element, locale);
+ if (product.setminimumActivePeriod(minimumActivePeriod)) {
+ actualChanges.put(ShareProductApiConstants.minimumactiveperiodfordividends_paramname, minimumActivePeriod);
+ }
+ }
+
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.minimumactiveperiodfrequencytype_paramname, element)) {
+ PeriodFrequencyType minimumActivePeriodType = extractPeriodType(
+ ShareProductApiConstants.minimumactiveperiodfrequencytype_paramname, element);
+ if (minimumActivePeriod != null) {
+ baseDataValidator.reset().parameter(ShareProductApiConstants.minimumactiveperiodfrequencytype_paramname)
+ .value(minimumActivePeriodType.getValue()).integerSameAsNumber(PeriodFrequencyType.DAYS.getValue());
+ }
+ if (product.setminimumActivePeriodFrequencyType(minimumActivePeriodType)) {
+ actualChanges.put(ShareProductApiConstants.minimumactiveperiodfrequencytype_paramname, minimumActivePeriodType);
+ }
+ }
+ final Integer lockinPeriod;
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.lockperiod_paramname, element)) {
+ lockinPeriod = this.fromApiJsonHelper.extractIntegerNamed(ShareProductApiConstants.lockperiod_paramname, element, locale);
+ if (product.setLockinPeriod(lockinPeriod)) {
+ actualChanges.put(ShareProductApiConstants.lockperiod_paramname, lockinPeriod);
+ }
+ }
+
+ if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.lockinperiodfrequencytype_paramname, element)) {
+ PeriodFrequencyType lockPeriod = extractPeriodType(ShareProductApiConstants.lockinperiodfrequencytype_paramname, element);
+ if (product.setLockPeriodFrequencyType(lockPeriod)) {
+ actualChanges.put(ShareProductApiConstants.lockinperiodfrequencytype_paramname, lockPeriod);
+ }
+ }
+
+ BigDecimal shareCapitalValue;
+ if (sharesIssued != null || unitPrice != null) {
+ if (sharesIssued == null) sharesIssued = product.getSharesIssued();
+ if (unitPrice == null) unitPrice = product.getUnitPrice();
+ shareCapitalValue = BigDecimal.valueOf(sharesIssued).multiply(unitPrice);
+ if (product.setshareCapitalValue(shareCapitalValue)) {
+ actualChanges.put(ShareProductApiConstants.sharecapital_paramname, shareCapitalValue);
+ }
+ }
+
+ if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
+ return actualChanges;
+ }
+
+ public void validateDividendDetails(JsonCommand jsonCommand) {
+ if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); }
+ final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
+ this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(),
+ ShareProductApiConstants.supportedParametersForDivident);
+
+ final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+ final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
+ .resource("sharesproduct.dividend.processing");
+
+ JsonElement element = jsonCommand.parsedJson();
+
+ final LocalDate dividendPeriodStartDate = this.fromApiJsonHelper.extractLocalDateNamed(
+ ShareProductApiConstants.dividendPeriodStartDateParamName, element);
+ baseDataValidator.reset().parameter(ShareProductApiConstants.dividendPeriodStartDateParamName).value(dividendPeriodStartDate)
+ .notBlank();
+
+ final LocalDate dividendPeriodEndDate = this.fromApiJsonHelper.extractLocalDateNamed(
+ ShareProductApiConstants.dividendPeriodEndDateParamName, element);
+ baseDataValidator.reset().parameter(ShareProductApiConstants.dividendPeriodStartDateParamName).value(dividendPeriodEndDate)
+ .notBlank().validateDateAfter(dividendPeriodStartDate);
+ final BigDecimal dividendAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
+ ShareProductApiConstants.dividendAmountParamName, element);
+ baseDataValidator.reset().parameter(ShareProductApiConstants.dividendAmountParamName).value(dividendAmount).notBlank()
+ .positiveAmount();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/08c553f9/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductCommandsServiceImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductCommandsServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductCommandsServiceImpl.java
new file mode 100644
index 0000000..ee36863
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductCommandsServiceImpl.java
@@ -0,0 +1,59 @@
+/**
+ * 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.shareproducts.service;
+
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.portfolio.products.service.ProductCommandsService;
+import org.apache.fineract.portfolio.shareproducts.constants.ShareProductApiConstants;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.google.gson.JsonElement;
+
+@Service(value = "SHAREPRODUCT_COMMANDSERVICE")
+public class ShareProductCommandsServiceImpl implements ProductCommandsService {
+
+ private final FromJsonHelper fromApiJsonHelper;
+
+ @Autowired
+ public ShareProductCommandsServiceImpl(final FromJsonHelper fromApiJsonHelper) {
+ this.fromApiJsonHelper = fromApiJsonHelper;
+ }
+
+
+ public CommandProcessingResult postDividends(Long productId, JsonCommand jsonCommand) {
+ return null ;
+ }
+
+ @Override
+ public Object handleCommand(Long productId, String command, String jsonBody) {
+ final JsonElement parsedCommand = this.fromApiJsonHelper.parse(jsonBody);
+ final JsonCommand jsonCommand = JsonCommand.from(jsonBody, parsedCommand, this.fromApiJsonHelper, null, null, null, null, null,
+ null, null, null, null, null);
+ if (ShareProductApiConstants.PREIEW_DIVIDENDS_COMMAND_STRING.equals(command)) {
+ return null ;
+ } else if (ShareProductApiConstants.POST_DIVIDENdS_COMMAND_STRING.equals(command)) { return postDividends(productId,
+ jsonCommand); }
+ // throw unknow commandexception
+ return CommandProcessingResult.empty();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/08c553f9/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendAssembler.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendAssembler.java
new file mode 100644
index 0000000..1ff71c7
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendAssembler.java
@@ -0,0 +1,131 @@
+/**
+ * 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.shareproducts.service;
+
+import java.math.BigDecimal;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
+import org.apache.fineract.organisation.monetary.domain.Money;
+import org.apache.fineract.portfolio.products.service.ProductReadPlatformService;
+import org.apache.fineract.portfolio.shareaccounts.data.PurchasedSharesData;
+import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountData;
+import org.apache.fineract.portfolio.shareaccounts.domain.PurchasedSharesStatusType;
+import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountDividendDetails;
+import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountReadPlatformService;
+import org.apache.fineract.portfolio.shareproducts.data.ShareProductData;
+import org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividendPayOutDetails;
+import org.joda.time.Days;
+import org.joda.time.LocalDate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ShareProductDividendAssembler {
+
+ private final ProductReadPlatformService shareProductReadPlatformService;
+ private final ShareAccountReadPlatformService ShareAccountReadPlatformService;
+
+ @Autowired
+ public ShareProductDividendAssembler(final ShareProductReadPlatformServiceImpl shareProductReadPlatformService,
+ final ShareAccountReadPlatformService ShareAccountReadPlatformService) {
+ this.shareProductReadPlatformService = shareProductReadPlatformService;
+ this.ShareAccountReadPlatformService = ShareAccountReadPlatformService;
+ }
+
+ public ShareProductDividendPayOutDetails calculateDividends(final Long productId, final BigDecimal amount,
+ final LocalDate dividendPeriodStartDate, final LocalDate dividendPeriodEndDate) {
+
+ ShareProductData product = (ShareProductData) this.shareProductReadPlatformService.retrieveOne(productId, false);
+ MonetaryCurrency currency = new MonetaryCurrency(product.getCurrency().code(), product.getCurrency().decimalPlaces(), product
+ .getCurrency().currencyInMultiplesOf());
+ Collection<ShareAccountData> shareAccountDatas = this.ShareAccountReadPlatformService.retrieveAllShareAccountDataForDividends(
+ productId, product.getAllowDividendCalculationForInactiveClients(), dividendPeriodStartDate);
+ ShareProductDividendPayOutDetails productDividendPayOutDetails = null;
+ final int minimumActivePeriod = product.getMinimumActivePeriod();
+ final Map<Long, Long> numberOfSharesdaysPerAccount = new HashMap<>();
+ long numberOfShareDays = calculateNumberOfShareDays(dividendPeriodEndDate, dividendPeriodStartDate, minimumActivePeriod,
+ shareAccountDatas, numberOfSharesdaysPerAccount);
+
+ if (numberOfShareDays > 0) {
+ double amountPerShareDay = amount.doubleValue() / numberOfShareDays;
+ productDividendPayOutDetails = new ShareProductDividendPayOutDetails(productId, Money.of(currency, amount).getAmount(),
+ dividendPeriodStartDate.toDate(), dividendPeriodEndDate.toDate());
+ for (ShareAccountData accountData : shareAccountDatas) {
+ long numberOfShareDaysPerAccount = numberOfSharesdaysPerAccount.get(accountData.getId());
+ double amountForAccount = numberOfShareDaysPerAccount * amountPerShareDay;
+ final Money accountAmount = Money.of(currency, BigDecimal.valueOf(amountForAccount));
+ ShareAccountDividendDetails dividendDetails = new ShareAccountDividendDetails(accountData.getId(),
+ accountAmount.getAmount());
+ productDividendPayOutDetails.getAccountDividendDetails().add(dividendDetails);
+ }
+ }
+
+ return productDividendPayOutDetails;
+ }
+
+ private long calculateNumberOfShareDays(final LocalDate postingDate, final LocalDate lastDividendPostDate,
+ final int minimumActivePeriod, final Collection<ShareAccountData> shareAccountDatas,
+ final Map<Long, Long> numberOfSharesdaysPerAccount) {
+ long numberOfShareDays = 0;
+ for (ShareAccountData accountData : shareAccountDatas) {
+ long numberOfShareDaysPerAccount = 0;
+ Collection<PurchasedSharesData> purchasedShares = accountData.getPurchasedShares();
+ long numberOfShares = 0;
+ LocalDate lastDividendAppliedDate = null;
+ for (PurchasedSharesData purchasedSharesData : purchasedShares) {
+ final PurchasedSharesStatusType status = PurchasedSharesStatusType.fromInt(purchasedSharesData.getStatus().getId()
+ .intValue());
+ final PurchasedSharesStatusType type = PurchasedSharesStatusType.fromInt(purchasedSharesData.getType().getId()
+ .intValue());
+ if (status.isApproved() && !type.isChargePayment()) {
+
+ LocalDate shareStartDate = purchasedSharesData.getPurchasedDate();
+ if (shareStartDate.isBefore(lastDividendPostDate)) {
+ shareStartDate = lastDividendPostDate;
+ }
+ int numberOfPurchseDays = Days.daysBetween(shareStartDate, postingDate).getDays();
+ if (type.isPurchased() && numberOfPurchseDays < minimumActivePeriod) {
+ continue;
+ }
+
+ if (lastDividendAppliedDate != null) {
+ numberOfShareDaysPerAccount += (Days.daysBetween(lastDividendAppliedDate, shareStartDate).getDays() * numberOfShares);
+ }
+ lastDividendAppliedDate = shareStartDate;
+ if (type.isPurchased()) {
+ numberOfShares += purchasedSharesData.getNumberOfShares();
+ } else {
+ numberOfShares -= purchasedSharesData.getNumberOfShares();
+ }
+
+ }
+ }
+ if (lastDividendAppliedDate != null) {
+ numberOfShareDaysPerAccount += (Days.daysBetween(lastDividendAppliedDate, postingDate).getDays() * numberOfShares);
+ }
+ numberOfShareDays += numberOfShareDaysPerAccount;
+ numberOfSharesdaysPerAccount.put(accountData.getId(), numberOfShareDaysPerAccount);
+ }
+ return numberOfShareDays;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/08c553f9/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendReadPlatformService.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendReadPlatformService.java
new file mode 100644
index 0000000..18eba3a
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendReadPlatformService.java
@@ -0,0 +1,29 @@
+/**
+ * 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.shareproducts.service;
+
+import org.apache.fineract.infrastructure.core.service.Page;
+import org.apache.fineract.infrastructure.core.service.SearchParameters;
+import org.apache.fineract.portfolio.shareproducts.data.ShareProductDividendPayOutData;
+
+public interface ShareProductDividendReadPlatformService {
+
+ Page<ShareProductDividendPayOutData> retriveAll(Long productId, Integer status, SearchParameters searchParameters);
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/08c553f9/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendReadPlatformServiceImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendReadPlatformServiceImpl.java
new file mode 100644
index 0000000..6760ef9
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendReadPlatformServiceImpl.java
@@ -0,0 +1,128 @@
+/**
+ * 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.shareproducts.service;
+
+import java.math.BigDecimal;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
+import org.apache.fineract.infrastructure.core.service.Page;
+import org.apache.fineract.infrastructure.core.service.PaginationHelper;
+import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
+import org.apache.fineract.infrastructure.core.service.SearchParameters;
+import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountDividendData;
+import org.apache.fineract.portfolio.shareaccounts.service.SharesEnumerations;
+import org.apache.fineract.portfolio.shareproducts.data.ShareProductDividendPayOutData;
+import org.apache.fineract.portfolio.shareproducts.data.ShareProductData;
+import org.joda.time.LocalDate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ShareProductDividendReadPlatformServiceImpl implements ShareProductDividendReadPlatformService {
+
+ private final JdbcTemplate jdbcTemplate;
+ private final PaginationHelper<ShareProductDividendPayOutData> paginationHelper = new PaginationHelper<>();
+
+ @Autowired
+ public ShareProductDividendReadPlatformServiceImpl(final RoutingDataSource dataSource) {
+ this.jdbcTemplate = new JdbcTemplate(dataSource);
+ }
+
+ @Override
+ public Page<ShareProductDividendPayOutData> retriveAll(final Long productId, final Integer status,
+ final SearchParameters searchParameters) {
+ ShareProductDividendMapper shareProductDividendMapper = new ShareProductDividendMapper();
+ final StringBuilder sqlBuilder = new StringBuilder(200);
+ sqlBuilder.append("select SQL_CALC_FOUND_ROWS ");
+ sqlBuilder.append(shareProductDividendMapper.schema());
+ sqlBuilder.append(" where sp.id = ? ");
+ List<Object> params = new ArrayList<>(2);
+ params.add(productId);
+ if (status != null) {
+ sqlBuilder.append(" and pod.status = ?");
+ params.add(status);
+ }
+ if (searchParameters.isOrderByRequested()) {
+ sqlBuilder.append(" order by ").append(searchParameters.getOrderBy());
+
+ if (searchParameters.isSortOrderProvided()) {
+ sqlBuilder.append(' ').append(searchParameters.getSortOrder());
+ }
+ }
+
+ if (searchParameters.isLimited()) {
+ sqlBuilder.append(" limit ").append(searchParameters.getLimit());
+ if (searchParameters.isOffset()) {
+ sqlBuilder.append(" offset ").append(searchParameters.getOffset());
+ }
+ }
+
+ final String sqlCountRows = "SELECT FOUND_ROWS()";
+ Object[] paramsObj = params.toArray();
+ return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), paramsObj,
+ shareProductDividendMapper);
+ }
+
+ private static final class ShareProductDividendMapper implements RowMapper<ShareProductDividendPayOutData> {
+
+ private final String sql;
+
+ public ShareProductDividendMapper() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(" pod.id as id, pod.amount as amount,");
+ sb.append(" pod.status as status, pod.dividend_period_start_date as startDate,");
+ sb.append(" pod.dividend_period_end_date as endDate,");
+ sb.append(" sp.id as productId,sp.name as productName ");
+ sb.append(" from m_share_product_dividend_pay_out pod");
+ sb.append(" inner join m_share_product sp on sp.id = pod.product_id ");
+ sql = sb.toString();
+ }
+
+ public String schema() {
+ return this.sql;
+ }
+
+ @Override
+ public ShareProductDividendPayOutData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
+ final Long id = rs.getLong("id");
+ final BigDecimal amount = rs.getBigDecimal("amount");
+ final Integer status = JdbcSupport.getInteger(rs, "status");
+ final EnumOptionData statusEnum = SharesEnumerations.ShareProductDividendStatusEnum(status);
+ final LocalDate startDate = JdbcSupport.getLocalDate(rs, "startDate");
+ final LocalDate endDate = JdbcSupport.getLocalDate(rs, "endDate");
+
+ final Long productId = rs.getLong("productId");
+ final String productName = rs.getString("productName");
+
+ final ShareProductData productData = ShareProductData.lookup(productId, productName);
+ final Collection<ShareAccountDividendData> accountDividendsData = null;
+ return new ShareProductDividendPayOutData(id, productData, amount, startDate, endDate, accountDividendsData, statusEnum);
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/08c553f9/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDropdownReadPlatformService.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDropdownReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDropdownReadPlatformService.java
new file mode 100644
index 0000000..ae3f98f
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDropdownReadPlatformService.java
@@ -0,0 +1,30 @@
+/**
+ * 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.shareproducts.service;
+
+import java.util.Collection;
+
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+
+public interface ShareProductDropdownReadPlatformService {
+
+ Collection<EnumOptionData> retrieveLockinPeriodFrequencyTypeOptions();
+
+ Collection<EnumOptionData> retrieveMinimumActivePeriodFrequencyTypeOptions() ;
+}
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/08c553f9/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDropdownReadPlatformServiceImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDropdownReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDropdownReadPlatformServiceImpl.java
new file mode 100644
index 0000000..c7c7b80
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDropdownReadPlatformServiceImpl.java
@@ -0,0 +1,53 @@
+/**
+ * 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.shareproducts.service;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.apache.fineract.portfolio.shareaccounts.service.SharesEnumerations;
+import org.apache.fineract.portfolio.shareproducts.SharePeriodFrequencyType;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ShareProductDropdownReadPlatformServiceImpl implements ShareProductDropdownReadPlatformService {
+
+ @Override
+ public Collection<EnumOptionData> retrieveLockinPeriodFrequencyTypeOptions() {
+ final List<EnumOptionData> allowedLockinPeriodFrequencyTypeOptions = Arrays.asList( //
+ SharesEnumerations.lockinPeriodFrequencyType(SharePeriodFrequencyType.DAYS), //
+ SharesEnumerations.lockinPeriodFrequencyType(SharePeriodFrequencyType.WEEKS), //
+ SharesEnumerations.lockinPeriodFrequencyType(SharePeriodFrequencyType.MONTHS), //
+ SharesEnumerations.lockinPeriodFrequencyType(SharePeriodFrequencyType.YEARS) //
+ );
+
+ return allowedLockinPeriodFrequencyTypeOptions;
+ }
+
+ @Override
+ public Collection<EnumOptionData> retrieveMinimumActivePeriodFrequencyTypeOptions() {
+ final List<EnumOptionData> minimumActivePeriodFrequencyTypeOptions = Arrays.asList( //
+ SharesEnumerations.lockinPeriodFrequencyType(SharePeriodFrequencyType.DAYS) //
+ );
+
+ return minimumActivePeriodFrequencyTypeOptions;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/08c553f9/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductReadPlatformServiceImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductReadPlatformServiceImpl.java
new file mode 100644
index 0000000..cd3b42e
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductReadPlatformServiceImpl.java
@@ -0,0 +1,283 @@
+/**
+ * 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.shareproducts.service;
+
+import java.math.BigDecimal;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.fineract.accounting.common.AccountingDropdownReadPlatformService;
+import org.apache.fineract.accounting.common.AccountingEnumerations;
+import org.apache.fineract.accounting.glaccount.data.GLAccountData;
+import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper;
+import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
+import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingReadPlatformService;
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
+import org.apache.fineract.infrastructure.core.service.Page;
+import org.apache.fineract.infrastructure.core.service.PaginationHelper;
+import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
+import org.apache.fineract.organisation.monetary.data.CurrencyData;
+import org.apache.fineract.organisation.monetary.service.CurrencyReadPlatformService;
+import org.apache.fineract.portfolio.charge.data.ChargeData;
+import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService;
+import org.apache.fineract.portfolio.products.data.ProductData;
+import org.apache.fineract.portfolio.products.exception.ProductNotFoundException;
+import org.apache.fineract.portfolio.products.service.ProductReadPlatformService;
+import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
+import org.apache.fineract.portfolio.shareproducts.data.ShareProductMarketPriceData;
+import org.apache.fineract.portfolio.shareproducts.data.ShareProductData;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+
+@Service(value = "shareReadPlatformService")
+public class ShareProductReadPlatformServiceImpl implements ProductReadPlatformService {
+
+ private final JdbcTemplate jdbcTemplate;
+ private final CurrencyReadPlatformService currencyReadPlatformService;
+ private final ChargeReadPlatformService chargeReadPlatformService;
+ private final ShareProductDropdownReadPlatformService shareProductDropdownReadPlatformService;
+ private final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService;
+ private final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService;
+ private final PaginationHelper<ProductData> provisioningEntryDataPaginationHelper = new PaginationHelper<>();
+
+ @Autowired
+ public ShareProductReadPlatformServiceImpl(final RoutingDataSource dataSource,
+ final CurrencyReadPlatformService currencyReadPlatformService, final ChargeReadPlatformService chargeReadPlatformService,
+ final ShareProductDropdownReadPlatformService shareProductDropdownReadPlatformService,
+ final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService,
+ final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService) {
+ this.jdbcTemplate = new JdbcTemplate(dataSource);
+ this.currencyReadPlatformService = currencyReadPlatformService;
+ this.chargeReadPlatformService = chargeReadPlatformService;
+ this.shareProductDropdownReadPlatformService = shareProductDropdownReadPlatformService;
+ this.accountingDropdownReadPlatformService = accountingDropdownReadPlatformService;
+ this.accountMappingReadPlatformService = accountMappingReadPlatformService;
+ }
+
+ @Override
+ public Page<ProductData> retrieveAllProducts(Integer offSet, Integer limit) {
+ AllShareProductRowMapper mapper = new AllShareProductRowMapper();
+ StringBuilder sqlBuilder = new StringBuilder();
+ sqlBuilder.append("select SQL_CALC_FOUND_ROWS ");
+ sqlBuilder.append(mapper.schema());
+ if (limit != null) {
+ sqlBuilder.append(" limit ").append(limit);
+ }
+ if (offSet != null) {
+ sqlBuilder.append(" offset ").append(offSet);
+ }
+
+ final String sqlCountRows = "SELECT FOUND_ROWS()";
+ Object[] whereClauseItemsitems = new Object[] {};
+ return this.provisioningEntryDataPaginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(),
+ whereClauseItemsitems, mapper);
+ }
+
+ @Override
+ public ProductData retrieveOne(Long productId, boolean includeTemplate) {
+ MarketPriceRowMapper marketRowMapper = new MarketPriceRowMapper();
+
+ try {
+ final String sql1 = "select " + marketRowMapper.schema() + " where marketData.product_id = ?";
+ final Collection<ShareProductMarketPriceData> shareMarketCollection = this.jdbcTemplate.query(sql1, marketRowMapper,
+ new Object[] { productId });
+ final Collection<ChargeData> charges = this.chargeReadPlatformService.retrieveShareProductCharges(productId);
+ ShareProductRowMapper mapper = new ShareProductRowMapper(shareMarketCollection, charges);
+ final String sql = "select " + mapper.schema() + " where shareproduct.id = ?";
+ ShareProductData data = (ShareProductData) this.jdbcTemplate.queryForObject(sql, mapper, new Object[] { productId });
+
+ if (data.hasAccountingEnabled()) {
+ final Map<String, Object> accountingMappings = this.accountMappingReadPlatformService
+ .fetchAccountMappingDetailsForShareProduct(productId, data.accountingRuleTypeId());
+ final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings = this.accountMappingReadPlatformService
+ .fetchPaymentTypeToFundSourceMappingsForShareProduct(productId);
+ Collection<ChargeToGLAccountMapper> feeToGLAccountMappings = this.accountMappingReadPlatformService
+ .fetchFeeToIncomeAccountMappingsForShareProduct(productId);
+ data = ShareProductData.withAccountingDetails(data, accountingMappings, paymentChannelToFundSourceMappings,
+ feeToGLAccountMappings);
+ }
+
+ if (includeTemplate) {
+ Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveSharesApplicableCharges();
+ final Collection<CurrencyData> currencyOptions = this.currencyReadPlatformService.retrieveAllowedCurrencies();
+ final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = this.shareProductDropdownReadPlatformService
+ .retrieveLockinPeriodFrequencyTypeOptions();
+ final Collection<EnumOptionData> minimumActivePeriodFrequencyTypeOptions = this.shareProductDropdownReadPlatformService
+ .retrieveMinimumActivePeriodFrequencyTypeOptions();
+ final Map<String, List<GLAccountData>> accountingMappingOptions = this.accountingDropdownReadPlatformService
+ .retrieveAccountMappingOptionsForShareProducts();
+ data = ShareProductData.template(data, currencyOptions, chargeOptions, minimumActivePeriodFrequencyTypeOptions,
+ lockinPeriodFrequencyTypeOptions, accountingMappingOptions);
+ }
+ return data;
+ } catch (final EmptyResultDataAccessException e) {
+ throw new ProductNotFoundException(productId, "share");
+ }
+ }
+
+ @Override
+ public ProductData retrieveTemplate() {
+ Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveSharesApplicableCharges();
+ final Collection<CurrencyData> currencyOptions = this.currencyReadPlatformService.retrieveAllowedCurrencies();
+ final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = this.shareProductDropdownReadPlatformService
+ .retrieveLockinPeriodFrequencyTypeOptions();
+ final Collection<EnumOptionData> minimumActivePeriodFrequencyTypeOptions = this.shareProductDropdownReadPlatformService
+ .retrieveMinimumActivePeriodFrequencyTypeOptions();
+ final Map<String, List<GLAccountData>> accountingMappingOptions = this.accountingDropdownReadPlatformService
+ .retrieveAccountMappingOptionsForShareProducts();
+ return ShareProductData.template(currencyOptions, chargeOptions, minimumActivePeriodFrequencyTypeOptions,
+ lockinPeriodFrequencyTypeOptions, accountingMappingOptions);
+ }
+
+ @Override
+ public Collection<ProductData> retrieveAllForLookup() {
+ AllShareProductRowMapper mapper = new AllShareProductRowMapper();
+ String sql = "select " + mapper.schema();
+ return this.jdbcTemplate.query(sql, mapper, new Object[] {});
+ }
+
+ @Override
+ public Set<String> getResponseDataParams() {
+ return null;
+ }
+
+ private static final class AllShareProductRowMapper implements RowMapper<ProductData> {
+
+ @Override
+ public ShareProductData mapRow(ResultSet rs, int rowNum) throws SQLException {
+ final Long id = rs.getLong("id");
+ final String name = rs.getString("name");
+ final String shortName = rs.getString("short_name");
+ final Long totalShares = rs.getLong("total_shares");
+ return ShareProductData.generic(id, name, shortName, totalShares);
+ }
+
+ public String schema() {
+ return "shareproduct.id, shareproduct.name, shareproduct.short_name, shareproduct.total_shares from m_share_product shareproduct";
+ }
+ }
+
+ private static final class MarketPriceRowMapper implements RowMapper<ShareProductMarketPriceData> {
+
+ @Override
+ public ShareProductMarketPriceData mapRow(ResultSet rs, int rowNum) throws SQLException {
+ final Long id = rs.getLong("id");
+ final Date fromDate = rs.getDate("from_date");
+ final BigDecimal shareValue = rs.getBigDecimal("share_value");
+ return new ShareProductMarketPriceData(id, fromDate, shareValue);
+ }
+
+ public String schema() {
+ return "marketData.id, marketData.from_date, marketData.share_value from m_share_product_market_price marketData";
+ }
+ }
+
+ private final static class ShareProductRowMapper implements RowMapper<ProductData> {
+
+ Collection<ShareProductMarketPriceData> shareMarketCollection;
+ Collection<ChargeData> charges;
+ private StringBuffer buff = new StringBuffer();
+
+ ShareProductRowMapper(Collection<ShareProductMarketPriceData> shareMarketCollection, Collection<ChargeData> charges) {
+ this.shareMarketCollection = shareMarketCollection;
+ this.charges = charges;
+ buff.append("shareproduct.id, shareproduct.name, shareproduct.short_name, ")
+ .append("shareproduct.external_id, shareproduct.description, shareproduct.start_date,")
+ .append("shareproduct.end_date, shareproduct.currency_code, shareproduct.currency_digits, ")
+ .append("shareproduct.currency_multiplesof, shareproduct.total_shares, shareproduct.issued_shares, ")
+ .append("shareproduct.unit_price, shareproduct.capital_amount, ")
+ .append("shareproduct.accounting_type as accountingType, ")
+ .append("shareproduct.minimum_client_shares, shareproduct.nominal_client_shares, ")
+ .append("shareproduct.maximum_client_shares, shareproduct.minimum_active_period_frequency, ")
+ .append("shareproduct.minimum_active_period_frequency_enum, shareproduct.lockin_period_frequency, ")
+ .append("shareproduct.lockin_period_frequency_enum, shareproduct.allow_dividends_inactive_clients, ")
+ .append("shareproduct.createdby_id, created.username as createdName, modified.username as modifiedName, ")
+ .append("shareproduct.created_date, shareproduct.lastmodifiedby_id, shareproduct.lastmodified_date, ")
+ .append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ")
+ .append("curr.display_symbol as currencyDisplaySymbol ").append("from m_share_product shareproduct ")
+ .append("LEFT JOIN m_currency curr on curr.code = shareproduct.currency_code ")
+ .append("LEFT JOIN m_appuser created ON created.id = shareproduct.createdby_id ")
+ .append("LEFT JOIN m_appuser modified ON modified.id = shareproduct.lastmodifiedby_id ");
+
+ }
+
+ @Override
+ public ShareProductData mapRow(ResultSet rs, int rowNum) throws SQLException {
+ final Long id = rs.getLong("id");
+ final String name = rs.getString("name");
+ final String shortName = rs.getString("short_name");
+ final String externalId = rs.getString("external_id");
+ final String description = rs.getString("description");
+ final String currencyCode = rs.getString("currency_code");
+ final Integer currencyDigits = rs.getInt("currency_digits");
+ final String currencyName = rs.getString("currencyName");
+ final String currencyNameCode = rs.getString("currencyNameCode");
+ final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
+ final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "currency_multiplesof");
+ final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf,
+ currencyDisplaySymbol, currencyNameCode);
+
+ final Long totalShares = rs.getLong("total_shares");
+ final Long issuedShares = rs.getLong("issued_shares");
+ final BigDecimal unitPrice = rs.getBigDecimal("unit_price");
+ final BigDecimal capitalAmount = rs.getBigDecimal("capital_amount");
+ final Long minimumClientShares = JdbcSupport.getLong(rs, "minimum_client_shares");
+ final Long nominalClientShares = JdbcSupport.getLong(rs, "nominal_client_shares");
+ final Long maximumClientShares = JdbcSupport.getLong(rs, "maximum_client_shares");
+ final Boolean allowDividendsForInactiveClients = rs.getBoolean("allow_dividends_inactive_clients");
+ // final Long createdById = rs.getLong("createdby_id") ;
+ // final Date createdDate = rs.getDate("created_date") ;
+ // final Long modifiedById = rs.getLong("lastmodifiedby_id") ;
+ // final Date modifiedDate = rs.getDate("lastmodified_date") ;
+ final Integer minimumActivePeriod = JdbcSupport.getInteger(rs, "minimum_active_period_frequency");
+ final Integer minimumActviePeriodEnumValue = JdbcSupport.getInteger(rs, "minimum_active_period_frequency_enum");
+ EnumOptionData minimumActivePeriodType = null;
+ if (minimumActviePeriodEnumValue != null) {
+ minimumActivePeriodType = SavingsEnumerations.lockinPeriodFrequencyType(minimumActviePeriodEnumValue);
+ }
+
+ final Integer lockinPeriodFrequency = JdbcSupport.getInteger(rs, "lockin_period_frequency");
+ EnumOptionData lockinPeriodFrequencyType = null;
+ final Integer lockinPeriodFrequencyTypeValue = JdbcSupport.getInteger(rs, "lockin_period_frequency_enum");
+ if (lockinPeriodFrequencyTypeValue != null) {
+ lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(lockinPeriodFrequencyTypeValue);
+ }
+ final Integer accountingRuleId = JdbcSupport.getInteger(rs, "accountingType");
+ final EnumOptionData accountingRuleType = AccountingEnumerations.accountingRuleType(accountingRuleId);
+
+ return ShareProductData.data(id, name, shortName, description, externalId, currency, totalShares, issuedShares, unitPrice,
+ capitalAmount, minimumClientShares, nominalClientShares, maximumClientShares, shareMarketCollection, charges,
+ allowDividendsForInactiveClients, lockinPeriodFrequency, lockinPeriodFrequencyType, minimumActivePeriod,
+ minimumActivePeriodType, accountingRuleType);
+ }
+
+ public String schema() {
+ return this.buff.toString();
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/08c553f9/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformService.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformService.java
new file mode 100644
index 0000000..7bb8111
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformService.java
@@ -0,0 +1,35 @@
+/**
+ * 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.shareproducts.service;
+
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+
+public interface ShareProductWritePlatformService {
+
+ CommandProcessingResult createShareProduct(JsonCommand jsonCommand);
+
+ CommandProcessingResult updateProduct(Long productId, JsonCommand command);
+
+ CommandProcessingResult createShareProductDividend(Long productId, JsonCommand jsonCommand);
+
+ CommandProcessingResult approveShareProductDividend(Long PayOutDetailId);
+
+ CommandProcessingResult deleteShareProductDividend(Long PayOutDetailId);
+}
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/08c553f9/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformServiceJpaRepositoryImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformServiceJpaRepositoryImpl.java
new file mode 100644
index 0000000..e9aaac7
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformServiceJpaRepositoryImpl.java
@@ -0,0 +1,190 @@
+/**
+ * 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.shareproducts.service;
+
+import java.math.BigDecimal;
+import java.util.Map;
+
+import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
+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.exception.PlatformDataIntegrityException;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.portfolio.shareproducts.constants.ShareProductApiConstants;
+import org.apache.fineract.portfolio.shareproducts.domain.ShareProduct;
+import org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividendPayOutDetails;
+import org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividentPayOutDetailsRepositoryWrapper;
+import org.apache.fineract.portfolio.shareproducts.domain.ShareProductRepositoryWrapper;
+import org.apache.fineract.portfolio.shareproducts.exception.DividentProcessingException;
+import org.apache.fineract.portfolio.shareproducts.serialization.ShareProductDataSerializer;
+import org.joda.time.LocalDate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.stereotype.Service;
+
+import com.google.gson.JsonElement;
+
+@Service
+public class ShareProductWritePlatformServiceJpaRepositoryImpl implements ShareProductWritePlatformService {
+
+ private final ShareProductRepositoryWrapper repository;
+ private final ShareProductDataSerializer serializer;
+ private final FromJsonHelper fromApiJsonHelper;
+ private final ShareProductDividentPayOutDetailsRepositoryWrapper shareProductDividentPayOutDetailsRepository;
+ private final ShareProductDividendAssembler shareProductDividendAssembler;
+ private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService;
+
+ @Autowired
+ public ShareProductWritePlatformServiceJpaRepositoryImpl(final ShareProductRepositoryWrapper repository,
+ final ShareProductDataSerializer serializer, final FromJsonHelper fromApiJsonHelper,
+ final ShareProductDividentPayOutDetailsRepositoryWrapper shareProductDividentPayOutDetailsRepositor,
+ final ShareProductDividendAssembler shareProductDividendAssembler,
+ final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService) {
+ this.repository = repository;
+ this.serializer = serializer;
+ this.fromApiJsonHelper = fromApiJsonHelper;
+ this.shareProductDividentPayOutDetailsRepository = shareProductDividentPayOutDetailsRepositor;
+ this.shareProductDividendAssembler = shareProductDividendAssembler;
+ this.accountMappingWritePlatformService = accountMappingWritePlatformService;
+ }
+
+ @Override
+ public CommandProcessingResult createShareProduct(JsonCommand jsonCommand) {
+ try {
+ ShareProduct product = this.serializer.validateAndCreate(jsonCommand);
+ this.repository.save(product);
+
+ // save accounting mappings
+ this.accountMappingWritePlatformService.createShareProductToGLAccountMapping(product.getId(), jsonCommand);
+
+ return new CommandProcessingResultBuilder() //
+ .withCommandId(jsonCommand.commandId()) //
+ .withEntityId(product.getId()) //
+ .build();
+ } catch (final DataIntegrityViolationException dve) {
+ handleDataIntegrityIssues(jsonCommand, dve);
+ return CommandProcessingResult.empty();
+ }
+
+ }
+
+ @Override
+ public CommandProcessingResult updateProduct(Long productId, JsonCommand jsonCommand) {
+ try {
+ ShareProduct product = this.repository.findOneWithNotFoundDetection(productId);
+ final Map<String, Object> changes = this.serializer.validateAndUpdate(jsonCommand, product);
+
+ // accounting related changes
+ final boolean accountingTypeChanged = changes.containsKey(ShareProductApiConstants.accountingRuleParamName);
+ final Map<String, Object> accountingMappingChanges = this.accountMappingWritePlatformService
+ .updateShareProductToGLAccountMapping(product.getId(), jsonCommand, accountingTypeChanged, product.getAccountingType());
+ changes.putAll(accountingMappingChanges);
+
+ if (!changes.isEmpty()) {
+ this.repository.saveAndFlush(product);
+ }
+ return new CommandProcessingResultBuilder() //
+ .withCommandId(jsonCommand.commandId()) //
+ .withEntityId(productId) //
+ .with(changes) //
+ .build();
+ } catch (DataIntegrityViolationException dve) {
+ handleDataIntegrityIssues(jsonCommand, dve);
+ return CommandProcessingResult.empty();
+ }
+ }
+
+ @Override
+ public CommandProcessingResult createShareProductDividend(final Long productId, final JsonCommand jsonCommand) {
+ try {
+ this.serializer.validateDividendDetails(jsonCommand);
+ JsonElement element = jsonCommand.parsedJson();
+ final LocalDate dividendPeriodStartDate = this.fromApiJsonHelper.extractLocalDateNamed(
+ ShareProductApiConstants.dividendPeriodStartDateParamName, element);
+ final LocalDate dividendPeriodEndDate = this.fromApiJsonHelper.extractLocalDateNamed(
+ ShareProductApiConstants.dividendPeriodEndDateParamName, element);
+ final BigDecimal dividendAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
+ ShareProductApiConstants.dividendAmountParamName, element);
+
+ final ShareProductDividendPayOutDetails dividendPayOutDetails = this.shareProductDividendAssembler.calculateDividends(
+ productId, dividendAmount, dividendPeriodStartDate, dividendPeriodEndDate);
+ if (dividendPayOutDetails == null) { throw new DividentProcessingException("eligible.shares.not.found",
+ "No eligible shares for creating dividends"); }
+ this.shareProductDividentPayOutDetailsRepository.save(dividendPayOutDetails);
+
+ return new CommandProcessingResultBuilder() //
+ .withCommandId(jsonCommand.commandId()) //
+ .withEntityId(productId) //
+ .withSubEntityId(dividendPayOutDetails.getId())//
+ .build();
+ } catch (DataIntegrityViolationException dve) {
+ handleDataIntegrityIssues(jsonCommand, dve);
+ return CommandProcessingResult.empty();
+ }
+ }
+
+ @Override
+ public CommandProcessingResult approveShareProductDividend(final Long PayOutDetailId) {
+ try {
+ ShareProductDividendPayOutDetails dividendPayOutDetails = this.shareProductDividentPayOutDetailsRepository
+ .findOneWithNotFoundDetection(PayOutDetailId);
+ if (dividendPayOutDetails.getStatus().isApproved()) { throw new DividentProcessingException("alreay.approved",
+ "Can't approve already appoved dividends "); }
+ dividendPayOutDetails.approveDividendPayout();
+ this.shareProductDividentPayOutDetailsRepository.save(dividendPayOutDetails);
+ return new CommandProcessingResultBuilder() //
+ .withEntityId(PayOutDetailId) //
+ .build();
+ } catch (DataIntegrityViolationException dve) {
+ handleDataIntegrityIssues(dve);
+ return CommandProcessingResult.empty();
+ }
+ }
+
+ @Override
+ public CommandProcessingResult deleteShareProductDividend(final Long PayOutDetailId) {
+ try {
+ ShareProductDividendPayOutDetails dividendPayOutDetails = this.shareProductDividentPayOutDetailsRepository
+ .findOneWithNotFoundDetection(PayOutDetailId);
+ if (dividendPayOutDetails.getStatus().isApproved()) { throw new DividentProcessingException("alreay.approved",
+ "Can't delete already appoved dividends "); }
+ this.shareProductDividentPayOutDetailsRepository.delete(dividendPayOutDetails);
+ return new CommandProcessingResultBuilder() //
+ .withEntityId(PayOutDetailId) //
+ .build();
+ } catch (DataIntegrityViolationException dve) {
+ handleDataIntegrityIssues(dve);
+ return CommandProcessingResult.empty();
+ }
+ }
+
+ private void handleDataIntegrityIssues(final DataIntegrityViolationException dve) {
+ final Throwable realCause = dve.getMostSpecificCause();
+ throw new PlatformDataIntegrityException("error.msg.glClosure.unknown.data.integrity.issue",
+ "Unknown data integrity issue with resource GL Closure: " + realCause.getMessage());
+ }
+
+ private void handleDataIntegrityIssues(@SuppressWarnings("unused") final JsonCommand command, final DataIntegrityViolationException dve) {
+ final Throwable realCause = dve.getMostSpecificCause();
+ throw new PlatformDataIntegrityException("error.msg.glClosure.unknown.data.integrity.issue",
+ "Unknown data integrity issue with resource GL Closure: " + realCause.getMessage());
+ }
+
+}