Return-Path: X-Original-To: apmail-fineract-commits-archive@minotaur.apache.org Delivered-To: apmail-fineract-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 29CFA19360 for ; Wed, 13 Apr 2016 14:35:31 +0000 (UTC) Received: (qmail 72789 invoked by uid 500); 13 Apr 2016 14:35:31 -0000 Delivered-To: apmail-fineract-commits-archive@fineract.apache.org Received: (qmail 72757 invoked by uid 500); 13 Apr 2016 14:35:31 -0000 Mailing-List: contact commits-help@fineract.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@fineract.incubator.apache.org Delivered-To: mailing list commits@fineract.incubator.apache.org Received: (qmail 72748 invoked by uid 99); 13 Apr 2016 14:35:31 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd4-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 13 Apr 2016 14:35:31 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd4-us-west.apache.org (ASF Mail Server at spamd4-us-west.apache.org) with ESMTP id 67A4CC064A for ; Wed, 13 Apr 2016 14:35:30 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd4-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -3.221 X-Spam-Level: X-Spam-Status: No, score=-3.221 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RCVD_IN_DNSWL_HI=-5, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RP_MATCHES_RCVD=-0.001] autolearn=disabled Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd4-us-west.apache.org [10.40.0.11]) (amavisd-new, port 10024) with ESMTP id sJHrPjOYfNVl for ; Wed, 13 Apr 2016 14:35:17 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with SMTP id D95CA5F3F3 for ; Wed, 13 Apr 2016 14:35:14 +0000 (UTC) Received: (qmail 69304 invoked by uid 99); 13 Apr 2016 14:35:13 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 13 Apr 2016 14:35:13 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id B2CB3E022F; Wed, 13 Apr 2016 14:35:13 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: rajuan@apache.org To: commits@fineract.incubator.apache.org Date: Wed, 13 Apr 2016 14:35:15 -0000 Message-Id: <007200f8b6364b3bad66efeb7a3bb0c6@git.apache.org> In-Reply-To: <7710663d6214421dab62d4e990174261@git.apache.org> References: <7710663d6214421dab62d4e990174261@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [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>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), + ShareProductApiConstants.supportedParametersForCreate); + + final List 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 marketPriceSet = asembleShareMarketPrice(element); + Set 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 asembleShareMarketPriceForUpdate(final JsonElement element) { + Set 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 asembleShareMarketPrice(final JsonElement element) { + Set 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 assembleListOfProductCharges(final JsonElement element, final String currencyCode) { + final Set 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 validateAndUpdate(JsonCommand jsonCommand, ShareProduct product) { + Map actualChanges = new HashMap<>(); + + if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); } + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), + ShareProductApiConstants.supportedParametersForCreate); + + final List 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 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 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>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), + ShareProductApiConstants.supportedParametersForDivident); + + final List 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 shareAccountDatas = this.ShareAccountReadPlatformService.retrieveAllShareAccountDataForDividends( + productId, product.getAllowDividendCalculationForInactiveClients(), dividendPeriodStartDate); + ShareProductDividendPayOutDetails productDividendPayOutDetails = null; + final int minimumActivePeriod = product.getMinimumActivePeriod(); + final Map 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 shareAccountDatas, + final Map numberOfSharesdaysPerAccount) { + long numberOfShareDays = 0; + for (ShareAccountData accountData : shareAccountDatas) { + long numberOfShareDaysPerAccount = 0; + Collection 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 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 paginationHelper = new PaginationHelper<>(); + + @Autowired + public ShareProductDividendReadPlatformServiceImpl(final RoutingDataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + @Override + public Page 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 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 { + + 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 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 retrieveLockinPeriodFrequencyTypeOptions(); + + Collection 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 retrieveLockinPeriodFrequencyTypeOptions() { + final List allowedLockinPeriodFrequencyTypeOptions = Arrays.asList( // + SharesEnumerations.lockinPeriodFrequencyType(SharePeriodFrequencyType.DAYS), // + SharesEnumerations.lockinPeriodFrequencyType(SharePeriodFrequencyType.WEEKS), // + SharesEnumerations.lockinPeriodFrequencyType(SharePeriodFrequencyType.MONTHS), // + SharesEnumerations.lockinPeriodFrequencyType(SharePeriodFrequencyType.YEARS) // + ); + + return allowedLockinPeriodFrequencyTypeOptions; + } + + @Override + public Collection retrieveMinimumActivePeriodFrequencyTypeOptions() { + final List 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 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 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 shareMarketCollection = this.jdbcTemplate.query(sql1, marketRowMapper, + new Object[] { productId }); + final Collection 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 accountingMappings = this.accountMappingReadPlatformService + .fetchAccountMappingDetailsForShareProduct(productId, data.accountingRuleTypeId()); + final Collection paymentChannelToFundSourceMappings = this.accountMappingReadPlatformService + .fetchPaymentTypeToFundSourceMappingsForShareProduct(productId); + Collection feeToGLAccountMappings = this.accountMappingReadPlatformService + .fetchFeeToIncomeAccountMappingsForShareProduct(productId); + data = ShareProductData.withAccountingDetails(data, accountingMappings, paymentChannelToFundSourceMappings, + feeToGLAccountMappings); + } + + if (includeTemplate) { + Collection chargeOptions = this.chargeReadPlatformService.retrieveSharesApplicableCharges(); + final Collection currencyOptions = this.currencyReadPlatformService.retrieveAllowedCurrencies(); + final Collection lockinPeriodFrequencyTypeOptions = this.shareProductDropdownReadPlatformService + .retrieveLockinPeriodFrequencyTypeOptions(); + final Collection minimumActivePeriodFrequencyTypeOptions = this.shareProductDropdownReadPlatformService + .retrieveMinimumActivePeriodFrequencyTypeOptions(); + final Map> 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 chargeOptions = this.chargeReadPlatformService.retrieveSharesApplicableCharges(); + final Collection currencyOptions = this.currencyReadPlatformService.retrieveAllowedCurrencies(); + final Collection lockinPeriodFrequencyTypeOptions = this.shareProductDropdownReadPlatformService + .retrieveLockinPeriodFrequencyTypeOptions(); + final Collection minimumActivePeriodFrequencyTypeOptions = this.shareProductDropdownReadPlatformService + .retrieveMinimumActivePeriodFrequencyTypeOptions(); + final Map> accountingMappingOptions = this.accountingDropdownReadPlatformService + .retrieveAccountMappingOptionsForShareProducts(); + return ShareProductData.template(currencyOptions, chargeOptions, minimumActivePeriodFrequencyTypeOptions, + lockinPeriodFrequencyTypeOptions, accountingMappingOptions); + } + + @Override + public Collection retrieveAllForLookup() { + AllShareProductRowMapper mapper = new AllShareProductRowMapper(); + String sql = "select " + mapper.schema(); + return this.jdbcTemplate.query(sql, mapper, new Object[] {}); + } + + @Override + public Set getResponseDataParams() { + return null; + } + + private static final class AllShareProductRowMapper implements RowMapper { + + @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 { + + @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 { + + Collection shareMarketCollection; + Collection charges; + private StringBuffer buff = new StringBuffer(); + + ShareProductRowMapper(Collection shareMarketCollection, Collection 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 changes = this.serializer.validateAndUpdate(jsonCommand, product); + + // accounting related changes + final boolean accountingTypeChanged = changes.containsKey(ShareProductApiConstants.accountingRuleParamName); + final Map 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()); + } + +}