Merge remote-tracking branch 'origin/uate' into dev
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-5) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (main, cucumber) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (main, build-core) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (progressive-loan, build-progressive-loan) (push) Canceled after 0s
Fineract Docker Builds / build (false, docker-compose-postgresql.yml, postgresql) (push) Canceled after 0s
Fineract Docker Builds / build (true, docker-compose.yml, mariadb) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-5) (push) Canceled after 0s
Fineract Documentation build / build (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-5) (push) Canceled after 0s
Fineract Liquibase Only mode - PostgreSQL / test (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with ActiveMQ (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with Kafka (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 1 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 10 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 2 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 3 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 4 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 5 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 6 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 7 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 8 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 9 of 10) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-5) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (main, cucumber) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (main, build-core) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (progressive-loan, build-progressive-loan) (push) Canceled after 0s
Fineract Docker Builds / build (false, docker-compose-postgresql.yml, postgresql) (push) Canceled after 0s
Fineract Docker Builds / build (true, docker-compose.yml, mariadb) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-5) (push) Canceled after 0s
Fineract Documentation build / build (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-5) (push) Canceled after 0s
Fineract Liquibase Only mode - PostgreSQL / test (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with ActiveMQ (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with Kafka (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 1 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 10 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 2 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 3 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 4 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 5 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 6 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 7 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 8 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 9 of 10) (push) Canceled after 0s
This commit is contained in:
+7
-4
@@ -60,6 +60,8 @@ public interface ShareAccountApiConstants {
|
|||||||
|
|
||||||
String externalid_paramname = "externalId";
|
String externalid_paramname = "externalId";
|
||||||
|
|
||||||
|
String resolutionnumber_paramname = "resolutionNumber";
|
||||||
|
|
||||||
String currency_paramname = "currencyCode";
|
String currency_paramname = "currencyCode";
|
||||||
|
|
||||||
String digitsafterdecimal_paramname = "digitsAfterDecimal";
|
String digitsafterdecimal_paramname = "digitsAfterDecimal";
|
||||||
@@ -102,9 +104,10 @@ public interface ShareAccountApiConstants {
|
|||||||
|
|
||||||
Set<String> supportedParameters = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(locale_paramname, dateformat_paramname,
|
Set<String> supportedParameters = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(locale_paramname, dateformat_paramname,
|
||||||
id_paramname, clientid_paramname, productid_paramname, submitteddate_paramname, approveddate_paramname, externalid_paramname,
|
id_paramname, clientid_paramname, productid_paramname, submitteddate_paramname, approveddate_paramname, externalid_paramname,
|
||||||
currency_paramname, digitsafterdecimal_paramname, inmultiplesof_paramname, requestedshares_paramname,
|
resolutionnumber_paramname, currency_paramname, digitsafterdecimal_paramname, inmultiplesof_paramname,
|
||||||
savingsaccountid_paramname, lockinperiod_paramname, lockperiodfrequencytype_paramname, minimumactiveperiod_paramname,
|
requestedshares_paramname, savingsaccountid_paramname, lockinperiod_paramname, lockperiodfrequencytype_paramname,
|
||||||
minimumactiveperiodfrequencytype_paramname, allowdividendcalculationforinactiveclients_paramname, charges_paramname,
|
minimumactiveperiod_paramname, minimumactiveperiodfrequencytype_paramname,
|
||||||
applicationdate_param, purchaseddate_paramname, numberofshares_paramname, purchasedprice_paramname)));
|
allowdividendcalculationforinactiveclients_paramname, charges_paramname, applicationdate_param, purchaseddate_paramname,
|
||||||
|
numberofshares_paramname, purchasedprice_paramname)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -42,6 +42,7 @@ public class AccountRequest implements Serializable {
|
|||||||
private Long numberOfShares;
|
private Long numberOfShares;
|
||||||
private String allowDividendCalculationForInactiveClients;
|
private String allowDividendCalculationForInactiveClients;
|
||||||
private String externalId;
|
private String externalId;
|
||||||
|
private String resolutionNumber;
|
||||||
private String minimumActivePeriodFrequencyType;
|
private String minimumActivePeriodFrequencyType;
|
||||||
private Long savingsAccountId;
|
private Long savingsAccountId;
|
||||||
private String locale;
|
private String locale;
|
||||||
|
|||||||
+86
-6
@@ -92,10 +92,12 @@ import org.apache.fineract.portfolio.savings.data.SavingsAccountApplicationTimel
|
|||||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData;
|
import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData;
|
||||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
|
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
|
||||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountStatusEnumData;
|
import org.apache.fineract.portfolio.savings.data.SavingsAccountStatusEnumData;
|
||||||
|
import org.apache.fineract.portfolio.savings.data.SavingsAccountSubStatusEnumData;
|
||||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountSummaryData;
|
import org.apache.fineract.portfolio.savings.data.SavingsAccountSummaryData;
|
||||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
|
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
|
||||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionEnumData;
|
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionEnumData;
|
||||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType;
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccountSubStatusEnum;
|
||||||
import org.apache.fineract.portfolio.savings.exception.DepositAccountNotFoundException;
|
import org.apache.fineract.portfolio.savings.exception.DepositAccountNotFoundException;
|
||||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||||
import org.apache.fineract.useradministration.domain.AppUser;
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
@@ -847,6 +849,13 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
|
|||||||
selectFieldsSqlBuilder.append("sa.senior_citizen_interest_rate as seniorCitizenInterestRate, ");
|
selectFieldsSqlBuilder.append("sa.senior_citizen_interest_rate as seniorCitizenInterestRate, ");
|
||||||
selectFieldsSqlBuilder.append("sa.min_balance_for_interest_calculation as minBalanceForInterestCalculation, ");
|
selectFieldsSqlBuilder.append("sa.min_balance_for_interest_calculation as minBalanceForInterestCalculation, ");
|
||||||
selectFieldsSqlBuilder.append("sa.withhold_tax as withHoldTax,");
|
selectFieldsSqlBuilder.append("sa.withhold_tax as withHoldTax,");
|
||||||
|
selectFieldsSqlBuilder.append("sa.sub_status_enum as subStatusEnum, ");
|
||||||
|
selectFieldsSqlBuilder.append("sa.reason_for_block as reasonForBlock, ");
|
||||||
|
selectFieldsSqlBuilder.append("(select COALESCE(max(sat.transaction_date),sa.activatedon_date) ");
|
||||||
|
selectFieldsSqlBuilder.append("from m_savings_account_transaction as sat ");
|
||||||
|
selectFieldsSqlBuilder.append("where sat.is_reversed = false and sat.is_reversal = false ");
|
||||||
|
selectFieldsSqlBuilder.append("and sat.transaction_type_enum in (1,2) ");
|
||||||
|
selectFieldsSqlBuilder.append("and sat.savings_account_id = sa.id) as lastActiveTransactionDate, ");
|
||||||
selectFieldsSqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName ");
|
selectFieldsSqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName ");
|
||||||
|
|
||||||
this.selectFieldsSql = selectFieldsSqlBuilder.toString();
|
this.selectFieldsSql = selectFieldsSqlBuilder.toString();
|
||||||
@@ -1129,12 +1138,48 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
|
|||||||
preClosurePenalInterestOnType, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType,
|
preClosurePenalInterestOnType, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType,
|
||||||
inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod,
|
inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod,
|
||||||
depositPeriodFrequencyType, onAccountClosureType, transferInterestToSavings, transferToSavingsId);
|
depositPeriodFrequencyType, onAccountClosureType, transferInterestToSavings, transferToSavingsId);
|
||||||
|
|
||||||
|
// Live per-account dormancy state: subStatus + the actual countdown, mirroring
|
||||||
|
// SavingsAccountReadPlatformServiceImpl.SavingAccountMapper. The isDormancyTrackingActive/
|
||||||
|
// daysToInactive/daysToDormancy/daysToEscheat columns read below are the product's
|
||||||
|
// configured thresholds; they're used here only as inputs to compute the live countdown.
|
||||||
|
final Integer subStatusEnum = JdbcSupport.getInteger(rs, "subStatusEnum");
|
||||||
|
final SavingsAccountSubStatusEnumData subStatus = SavingsEnumerations.subStatus(subStatusEnum);
|
||||||
|
final String reasonForBlock = rs.getString("reasonForBlock");
|
||||||
|
final LocalDate lastActiveTransactionDate = JdbcSupport.getLocalDate(rs, "lastActiveTransactionDate");
|
||||||
|
final boolean isDormancyTrackingActive = rs.getBoolean("isDormancyTrackingActive");
|
||||||
|
final Integer numDaysToInactive = JdbcSupport.getInteger(rs, "daysToInactive");
|
||||||
|
final Integer numDaysToDormancy = JdbcSupport.getInteger(rs, "daysToDormancy");
|
||||||
|
final Integer numDaysToEscheat = JdbcSupport.getInteger(rs, "daysToEscheat");
|
||||||
|
Integer computedDaysToInactive = null;
|
||||||
|
Integer computedDaysToDormancy = null;
|
||||||
|
Integer computedDaysToEscheat = null;
|
||||||
|
final Integer accountStatusEnum = JdbcSupport.getInteger(rs, "statusEnum");
|
||||||
|
if (isDormancyTrackingActive && lastActiveTransactionDate != null && accountStatusEnum != null
|
||||||
|
&& accountStatusEnum.equals(SavingsAccountStatusType.ACTIVE.getValue())) {
|
||||||
|
final LocalDate currentDate = DateUtils.getBusinessLocalDate();
|
||||||
|
if (subStatusEnum < SavingsAccountSubStatusEnum.ESCHEAT.getValue()) {
|
||||||
|
computedDaysToEscheat = DateUtils.getExactDifferenceInDays(currentDate,
|
||||||
|
lastActiveTransactionDate.plusDays(numDaysToEscheat));
|
||||||
|
}
|
||||||
|
if (subStatusEnum < SavingsAccountSubStatusEnum.DORMANT.getValue()) {
|
||||||
|
computedDaysToDormancy = DateUtils.getExactDifferenceInDays(currentDate,
|
||||||
|
lastActiveTransactionDate.plusDays(numDaysToDormancy));
|
||||||
|
}
|
||||||
|
if (subStatusEnum < SavingsAccountSubStatusEnum.INACTIVE.getValue()) {
|
||||||
|
computedDaysToInactive = DateUtils.getExactDifferenceInDays(currentDate,
|
||||||
|
lastActiveTransactionDate.plusDays(numDaysToInactive));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
result.setCbsProductFields(rs.getString("accountType"), rs.getString("customerSegment"), rs.getString("fdType"), rs.getString("productCode"),
|
result.setCbsProductFields(rs.getString("accountType"), rs.getString("customerSegment"), rs.getString("fdType"), rs.getString("productCode"),
|
||||||
JdbcSupport.getLocalDate(rs, "effectiveDate"), JdbcSupport.getLocalDate(rs, "effectiveTo"),
|
JdbcSupport.getLocalDate(rs, "effectiveDate"), JdbcSupport.getLocalDate(rs, "effectiveTo"),
|
||||||
JdbcSupport.getLocalDate(rs, "endDate"), rs.getBoolean("introducerRequired"), rs.getBoolean("shareLinkingEnabled"),
|
JdbcSupport.getLocalDate(rs, "endDate"), rs.getBoolean("introducerRequired"), rs.getBoolean("shareLinkingEnabled"),
|
||||||
rs.getBoolean("nominationFacility"), rs.getBoolean("dormancyAlert"), rs.getBoolean("isDormancyTrackingActive"),
|
rs.getBoolean("nominationFacility"), rs.getBoolean("dormancyAlert"), isDormancyTrackingActive,
|
||||||
JdbcSupport.getLong(rs, "daysToInactive"), JdbcSupport.getLong(rs, "daysToDormancy"),
|
computedDaysToInactive == null ? null : computedDaysToInactive.longValue(),
|
||||||
JdbcSupport.getLong(rs, "daysToEscheat"), JdbcSupport.getInteger(rs, "interestCalculateSelector"),
|
computedDaysToDormancy == null ? null : computedDaysToDormancy.longValue(),
|
||||||
|
computedDaysToEscheat == null ? null : computedDaysToEscheat.longValue(),
|
||||||
|
JdbcSupport.getInteger(rs, "interestCalculateSelector"),
|
||||||
rs.getString("interestCalculationTypeName"), rs.getString("averagePeriod"), rs.getBoolean("autoPost"),
|
rs.getString("interestCalculationTypeName"), rs.getString("averagePeriod"), rs.getBoolean("autoPost"),
|
||||||
JdbcSupport.getInteger(rs, "frequencyFixDay"), JdbcSupport.getLocalDate(rs, "lastInterestDate"),
|
JdbcSupport.getInteger(rs, "frequencyFixDay"), JdbcSupport.getLocalDate(rs, "lastInterestDate"),
|
||||||
rs.getBoolean("enableLockinPeriod"), rs.getString("balanceFrequency"),
|
rs.getBoolean("enableLockinPeriod"), rs.getString("balanceFrequency"),
|
||||||
@@ -1144,6 +1189,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
|
|||||||
rs.getBoolean("creditMaturityProceeds"), rs.getBoolean("penaltyGracePeriodMissedInstallments"),
|
rs.getBoolean("creditMaturityProceeds"), rs.getBoolean("penaltyGracePeriodMissedInstallments"),
|
||||||
rs.getBoolean("panelRequestApplicable"), rs.getBoolean("noPenaltyNomineeHeirClosure"),
|
rs.getBoolean("panelRequestApplicable"), rs.getBoolean("noPenaltyNomineeHeirClosure"),
|
||||||
rs.getBoolean("deceasedSettlement"), rs.getBoolean("allowDebitTxnAfterDueDate"));
|
rs.getBoolean("deceasedSettlement"), rs.getBoolean("allowDebitTxnAfterDueDate"));
|
||||||
|
result.setDormancyStatus(subStatus, reasonForBlock, lastActiveTransactionDate);
|
||||||
result.setCbsAccountFields(rs.getString("accountStatus"), rs.getString("modeOfOperation"),
|
result.setCbsAccountFields(rs.getString("accountStatus"), rs.getString("modeOfOperation"),
|
||||||
JdbcSupport.getLong(rs, "introducerClientId"), rs.getString("introducerName"),
|
JdbcSupport.getLong(rs, "introducerClientId"), rs.getString("introducerName"),
|
||||||
rs.getString("introducerCifNumber"), rs.getString("specialCategory"), rs.getString("weakerSection"),
|
rs.getString("introducerCifNumber"), rs.getString("specialCategory"), rs.getString("weakerSection"),
|
||||||
@@ -1293,12 +1339,45 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
|
|||||||
onAccountClosureType, expectedFirstDepositOnDate, totalOverdueAmount, noOfOverdueInstallments, isMandatoryDeposit,
|
onAccountClosureType, expectedFirstDepositOnDate, totalOverdueAmount, noOfOverdueInstallments, isMandatoryDeposit,
|
||||||
allowWithdrawal, adjustAdvanceTowardsFuturePayments, isCalendarInherited);
|
allowWithdrawal, adjustAdvanceTowardsFuturePayments, isCalendarInherited);
|
||||||
|
|
||||||
|
// Live per-account dormancy state: subStatus + the actual countdown, mirroring
|
||||||
|
// SavingsAccountReadPlatformServiceImpl.SavingAccountMapper. The isDormancyTrackingActive/
|
||||||
|
// daysToInactive/daysToDormancy/daysToEscheat columns read below are the product's
|
||||||
|
// configured thresholds; they're used here only as inputs to compute the live countdown.
|
||||||
|
final Integer subStatusEnum = JdbcSupport.getInteger(rs, "subStatusEnum");
|
||||||
|
final SavingsAccountSubStatusEnumData subStatus = SavingsEnumerations.subStatus(subStatusEnum);
|
||||||
|
final String reasonForBlock = rs.getString("reasonForBlock");
|
||||||
|
final LocalDate lastActiveTransactionDate = JdbcSupport.getLocalDate(rs, "lastActiveTransactionDate");
|
||||||
|
final boolean isDormancyTrackingActive = rs.getBoolean("isDormancyTrackingActive");
|
||||||
|
final Integer numDaysToInactive = JdbcSupport.getInteger(rs, "daysToInactive");
|
||||||
|
final Integer numDaysToDormancy = JdbcSupport.getInteger(rs, "daysToDormancy");
|
||||||
|
final Integer numDaysToEscheat = JdbcSupport.getInteger(rs, "daysToEscheat");
|
||||||
|
Integer computedDaysToInactive = null;
|
||||||
|
Integer computedDaysToDormancy = null;
|
||||||
|
Integer computedDaysToEscheat = null;
|
||||||
|
final Integer accountStatusEnum = JdbcSupport.getInteger(rs, "statusEnum");
|
||||||
|
if (isDormancyTrackingActive && lastActiveTransactionDate != null && accountStatusEnum != null
|
||||||
|
&& accountStatusEnum.equals(SavingsAccountStatusType.ACTIVE.getValue())) {
|
||||||
|
final LocalDate currentDate = DateUtils.getBusinessLocalDate();
|
||||||
|
if (subStatusEnum < SavingsAccountSubStatusEnum.ESCHEAT.getValue()) {
|
||||||
|
computedDaysToEscheat = DateUtils.getExactDifferenceInDays(currentDate,
|
||||||
|
lastActiveTransactionDate.plusDays(numDaysToEscheat));
|
||||||
|
}
|
||||||
|
if (subStatusEnum < SavingsAccountSubStatusEnum.DORMANT.getValue()) {
|
||||||
|
computedDaysToDormancy = DateUtils.getExactDifferenceInDays(currentDate,
|
||||||
|
lastActiveTransactionDate.plusDays(numDaysToDormancy));
|
||||||
|
}
|
||||||
|
if (subStatusEnum < SavingsAccountSubStatusEnum.INACTIVE.getValue()) {
|
||||||
|
computedDaysToInactive = DateUtils.getExactDifferenceInDays(currentDate,
|
||||||
|
lastActiveTransactionDate.plusDays(numDaysToInactive));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
result.setCbsProductFields(rs.getString("accountType"), rs.getString("customerSegment"), rs.getString("productCode"),
|
result.setCbsProductFields(rs.getString("accountType"), rs.getString("customerSegment"), rs.getString("productCode"),
|
||||||
JdbcSupport.getLocalDate(rs, "effectiveDate"), JdbcSupport.getLocalDate(rs, "effectiveTo"),
|
JdbcSupport.getLocalDate(rs, "effectiveDate"), JdbcSupport.getLocalDate(rs, "effectiveTo"),
|
||||||
JdbcSupport.getLocalDate(rs, "endDate"), rs.getBoolean("introducerRequired"), rs.getBoolean("shareLinkingEnabled"),
|
JdbcSupport.getLocalDate(rs, "endDate"), rs.getBoolean("introducerRequired"), rs.getBoolean("shareLinkingEnabled"),
|
||||||
rs.getBoolean("nominationFacility"), rs.getBoolean("dormancyAlert"), rs.getBoolean("isDormancyTrackingActive"),
|
rs.getBoolean("nominationFacility"), rs.getBoolean("dormancyAlert"), isDormancyTrackingActive,
|
||||||
JdbcSupport.getInteger(rs, "daysToInactive"), JdbcSupport.getInteger(rs, "daysToDormancy"),
|
computedDaysToInactive, computedDaysToDormancy,
|
||||||
JdbcSupport.getInteger(rs, "daysToEscheat"), JdbcSupport.getInteger(rs, "interestCalculateSelector"),
|
computedDaysToEscheat, JdbcSupport.getInteger(rs, "interestCalculateSelector"),
|
||||||
rs.getString("interestCalculationTypeName"), rs.getString("averagePeriod"), rs.getBoolean("autoPost"),
|
rs.getString("interestCalculationTypeName"), rs.getString("averagePeriod"), rs.getBoolean("autoPost"),
|
||||||
JdbcSupport.getInteger(rs, "frequencyFixDay"), JdbcSupport.getLocalDate(rs, "lastInterestDate"),
|
JdbcSupport.getInteger(rs, "frequencyFixDay"), JdbcSupport.getLocalDate(rs, "lastInterestDate"),
|
||||||
rs.getBoolean("enableLockinPeriod"), rs.getString("balanceFrequency"),
|
rs.getBoolean("enableLockinPeriod"), rs.getString("balanceFrequency"),
|
||||||
@@ -1308,6 +1387,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
|
|||||||
rs.getBoolean("creditMaturityProceeds"), rs.getBoolean("penaltyGracePeriodMissedInstallments"),
|
rs.getBoolean("creditMaturityProceeds"), rs.getBoolean("penaltyGracePeriodMissedInstallments"),
|
||||||
rs.getBoolean("panelRequestApplicable"), rs.getBoolean("noPenaltyNomineeHeirClosure"),
|
rs.getBoolean("panelRequestApplicable"), rs.getBoolean("noPenaltyNomineeHeirClosure"),
|
||||||
rs.getBoolean("deceasedSettlement"), rs.getBoolean("allowDebitTxnAfterDueDate"));
|
rs.getBoolean("deceasedSettlement"), rs.getBoolean("allowDebitTxnAfterDueDate"));
|
||||||
|
result.setDormancyStatus(subStatus, reasonForBlock, lastActiveTransactionDate);
|
||||||
result.setCbsAccountFields(rs.getString("accountStatus"), rs.getString("modeOfOperation"),
|
result.setCbsAccountFields(rs.getString("accountStatus"), rs.getString("modeOfOperation"),
|
||||||
JdbcSupport.getLong(rs, "introducerClientId"), rs.getString("introducerName"),
|
JdbcSupport.getLong(rs, "introducerClientId"), rs.getString("introducerName"),
|
||||||
rs.getString("introducerCifNumber"), rs.getString("specialCategory"), rs.getString("weakerSection"),
|
rs.getString("introducerCifNumber"), rs.getString("specialCategory"), rs.getString("weakerSection"),
|
||||||
|
|||||||
+154
@@ -0,0 +1,154 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.shareaccounts.api;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.ws.rs.Consumes;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.POST;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.PathParam;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.core.Context;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.UriInfo;
|
||||||
|
import java.util.Collection;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||||
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||||
|
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||||
|
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||||
|
import org.apache.fineract.infrastructure.security.exception.NoAuthorizationException;
|
||||||
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountPendingEditData;
|
||||||
|
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountEditWritePlatformService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Path("/v1/shareaccounts")
|
||||||
|
@Component
|
||||||
|
@Tag(name = "Share Account Edit Approval", description = "Maker-checker workflow for editing a Share Account's details (linked savings account, dividend calculation for inactive clients, lockin period, minimum active period). Edits are held in m_share_account_pending_edit (a pending row there is itself the lock) and only applied to the real account once a checker approves.")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ShareAccountEditApiResource {
|
||||||
|
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
private final ShareAccountEditWritePlatformService shareAccountEditWritePlatformService;
|
||||||
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
|
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||||
|
private final DefaultToApiJsonSerializer<CommandProcessingResult> toApiJsonSerializer;
|
||||||
|
private final DefaultToApiJsonSerializer<ShareAccountPendingEditData> pendingEditApiJsonSerializer;
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("{accountId}/edit")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Submit a details edit for a Share Account",
|
||||||
|
description = "Edits are held in m_share_account_pending_edit (a pending row there is itself the lock) and only "
|
||||||
|
+ "applied to the real account once a checker approves - see /shareaccounts/{accountId}/edit/approve and /reject below.")
|
||||||
|
public String submitEdit(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId,
|
||||||
|
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
|
||||||
|
this.context.authenticatedUser().validateHasPermissionTo("SUBMITEDIT_SHAREACCOUNT");
|
||||||
|
|
||||||
|
final JsonCommand command = JsonCommand.fromJsonElement(accountId, this.fromApiJsonHelper.parse(apiRequestBodyAsJson),
|
||||||
|
this.fromApiJsonHelper);
|
||||||
|
|
||||||
|
final CommandProcessingResult result = this.shareAccountEditWritePlatformService.submitEditForApproval(accountId, command);
|
||||||
|
|
||||||
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("edit/pending")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "List Share Account details edits pending approval")
|
||||||
|
public String retrieveAllPendingEdits(@Context final UriInfo uriInfo) {
|
||||||
|
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission("SHAREACCOUNT");
|
||||||
|
|
||||||
|
final Collection<ShareAccountPendingEditData> pendingEdits = this.shareAccountEditWritePlatformService
|
||||||
|
.retrieveAllPendingEdits();
|
||||||
|
|
||||||
|
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||||
|
return this.pendingEditApiJsonSerializer.serialize(settings, pendingEdits);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{accountId}/edit/pending")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve the pending details edit for a Share Account, if any")
|
||||||
|
public String retrievePendingEdit(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId,
|
||||||
|
@Context final UriInfo uriInfo) {
|
||||||
|
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission("SHAREACCOUNT");
|
||||||
|
|
||||||
|
final ShareAccountPendingEditData pendingEdit = this.shareAccountEditWritePlatformService.retrievePendingEdit(accountId);
|
||||||
|
|
||||||
|
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||||
|
return this.pendingEditApiJsonSerializer.serialize(settings, pendingEdit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("{accountId}/edit/approve")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Approve a pending Share Account details edit",
|
||||||
|
description = "Applies the proposed details changes to the account. The maker of the edit cannot also approve it.")
|
||||||
|
public String approvePendingEdit(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId) {
|
||||||
|
|
||||||
|
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("APPROVEEDIT_SHAREACCOUNT")) {
|
||||||
|
throw new NoAuthorizationException("User has no authority to approve share account edits");
|
||||||
|
}
|
||||||
|
|
||||||
|
final CommandProcessingResult result = this.shareAccountEditWritePlatformService.approveEdit(accountId);
|
||||||
|
|
||||||
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("{accountId}/edit/reject")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Reject a pending Share Account details edit",
|
||||||
|
description = "Discards the proposed changes without applying them. The maker of the edit cannot also reject it.")
|
||||||
|
public String rejectPendingEdit(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId,
|
||||||
|
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
|
||||||
|
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("REJECTEDIT_SHAREACCOUNT")) {
|
||||||
|
throw new NoAuthorizationException("User has no authority to reject share account edits");
|
||||||
|
}
|
||||||
|
|
||||||
|
String rejectReason = null;
|
||||||
|
if (apiRequestBodyAsJson != null && !apiRequestBodyAsJson.isBlank()) {
|
||||||
|
final var parsed = this.fromApiJsonHelper.parse(apiRequestBodyAsJson);
|
||||||
|
if (parsed != null && parsed.isJsonObject() && parsed.getAsJsonObject().has("rejectReason")) {
|
||||||
|
rejectReason = parsed.getAsJsonObject().get("rejectReason").getAsString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final CommandProcessingResult result = this.shareAccountEditWritePlatformService.rejectEdit(accountId, rejectReason);
|
||||||
|
|
||||||
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
-15
@@ -41,6 +41,8 @@ public class ShareAccountData implements Serializable, AccountData {
|
|||||||
|
|
||||||
private String externalId;
|
private String externalId;
|
||||||
|
|
||||||
|
private String resolutionNumber;
|
||||||
|
|
||||||
private String savingsAccountNumber;
|
private String savingsAccountNumber;
|
||||||
|
|
||||||
private Long clientId;
|
private Long clientId;
|
||||||
@@ -154,9 +156,10 @@ public class ShareAccountData implements Serializable, AccountData {
|
|||||||
private Collection<EnumOptionData> minimumActivePeriodFrequencyTypeOptions;
|
private Collection<EnumOptionData> minimumActivePeriodFrequencyTypeOptions;
|
||||||
private Collection<SavingsAccountData> clientSavingsAccounts;
|
private Collection<SavingsAccountData> clientSavingsAccounts;
|
||||||
|
|
||||||
public ShareAccountData(final Long id, final String accountNo, final String externalId, final Long savingsAccountId,
|
public ShareAccountData(final Long id, final String accountNo, final String externalId, final String resolutionNumber,
|
||||||
final String savingsAccountNumber, final Long clientId, final String clientName, final Long productId, final String productName,
|
final Long savingsAccountId, final String savingsAccountNumber, final Long clientId, final String clientName,
|
||||||
final ShareAccountStatusEnumData status, final ShareAccountApplicationTimelineData timeline, final CurrencyData currency,
|
final Long productId, final String productName, final ShareAccountStatusEnumData status,
|
||||||
|
final ShareAccountApplicationTimelineData timeline, final CurrencyData currency,
|
||||||
final ShareAccountSummaryData summaryData, final Collection<ShareAccountChargeData> charges,
|
final ShareAccountSummaryData summaryData, final Collection<ShareAccountChargeData> charges,
|
||||||
final Collection<ShareAccountTransactionData> purchasedSharesData, final Integer lockinPeriod,
|
final Collection<ShareAccountTransactionData> purchasedSharesData, final Integer lockinPeriod,
|
||||||
final EnumOptionData lockPeriodTypeEnum, final Integer minimumActivePeriod, final EnumOptionData minimumActivePeriodTypeEnum,
|
final EnumOptionData lockPeriodTypeEnum, final Integer minimumActivePeriod, final EnumOptionData minimumActivePeriodTypeEnum,
|
||||||
@@ -164,6 +167,7 @@ public class ShareAccountData implements Serializable, AccountData {
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
this.accountNo = accountNo;
|
this.accountNo = accountNo;
|
||||||
this.externalId = externalId;
|
this.externalId = externalId;
|
||||||
|
this.resolutionNumber = resolutionNumber;
|
||||||
this.savingsAccountId = savingsAccountId;
|
this.savingsAccountId = savingsAccountId;
|
||||||
this.savingsAccountNumber = savingsAccountNumber;
|
this.savingsAccountNumber = savingsAccountNumber;
|
||||||
this.clientId = clientId;
|
this.clientId = clientId;
|
||||||
@@ -208,9 +212,10 @@ public class ShareAccountData implements Serializable, AccountData {
|
|||||||
this.defaultShares = defaultShares;
|
this.defaultShares = defaultShares;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ShareAccountData(final Long id, final String accountNo, final String externalId, final Long savingsAccountId,
|
private ShareAccountData(final Long id, final String accountNo, final String externalId, final String resolutionNumber,
|
||||||
final String savingsAccountNumber, final Long clientId, final String clientName, final Long productId, final String productName,
|
final Long savingsAccountId, final String savingsAccountNumber, final Long clientId, final String clientName,
|
||||||
final ShareAccountStatusEnumData status, final ShareAccountApplicationTimelineData timeline, final CurrencyData currency,
|
final Long productId, final String productName, final ShareAccountStatusEnumData status,
|
||||||
|
final ShareAccountApplicationTimelineData timeline, final CurrencyData currency,
|
||||||
final ShareAccountSummaryData summaryData, final Collection<ShareAccountChargeData> charges,
|
final ShareAccountSummaryData summaryData, final Collection<ShareAccountChargeData> charges,
|
||||||
final Collection<ShareAccountTransactionData> purchasedSharesData, final Integer lockinPeriod,
|
final Collection<ShareAccountTransactionData> purchasedSharesData, final Integer lockinPeriod,
|
||||||
final EnumOptionData lockPeriodTypeEnum, final Integer minimumActivePeriod, final EnumOptionData minimumActivePeriodTypeEnum,
|
final EnumOptionData lockPeriodTypeEnum, final Integer minimumActivePeriod, final EnumOptionData minimumActivePeriodTypeEnum,
|
||||||
@@ -221,6 +226,7 @@ public class ShareAccountData implements Serializable, AccountData {
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
this.accountNo = accountNo;
|
this.accountNo = accountNo;
|
||||||
this.externalId = externalId;
|
this.externalId = externalId;
|
||||||
|
this.resolutionNumber = resolutionNumber;
|
||||||
this.savingsAccountId = savingsAccountId;
|
this.savingsAccountId = savingsAccountId;
|
||||||
this.savingsAccountNumber = savingsAccountNumber;
|
this.savingsAccountNumber = savingsAccountNumber;
|
||||||
this.clientId = clientId;
|
this.clientId = clientId;
|
||||||
@@ -250,15 +256,17 @@ public class ShareAccountData implements Serializable, AccountData {
|
|||||||
Collection<ChargeData> chargeOptions, Collection<SavingsAccountData> clientSavingsAccounts,
|
Collection<ChargeData> chargeOptions, Collection<SavingsAccountData> clientSavingsAccounts,
|
||||||
Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions,
|
Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions,
|
||||||
Collection<EnumOptionData> minimumActivePeriodFrequencyTypeOptions) {
|
Collection<EnumOptionData> minimumActivePeriodFrequencyTypeOptions) {
|
||||||
return new ShareAccountData(data.id, data.accountNo, data.externalId, data.savingsAccountId, data.savingsAccountNumber,
|
return new ShareAccountData(data.id, data.accountNo, data.externalId, data.resolutionNumber, data.savingsAccountId,
|
||||||
data.clientId, data.clientName, data.productId, data.productName, data.status, data.timeline, data.currency, data.summary,
|
data.savingsAccountNumber, data.clientId, data.clientName, data.productId, data.productName, data.status, data.timeline,
|
||||||
data.charges, data.purchasedShares, data.lockinPeriod, data.lockPeriodTypeEnum, data.minimumActivePeriod,
|
data.currency, data.summary, data.charges, data.purchasedShares, data.lockinPeriod, data.lockPeriodTypeEnum,
|
||||||
data.minimumActivePeriodTypeEnum, data.allowDividendCalculationForInactiveClients, productOptions, chargeOptions,
|
data.minimumActivePeriod, data.minimumActivePeriodTypeEnum, data.allowDividendCalculationForInactiveClients,
|
||||||
clientSavingsAccounts, lockinPeriodFrequencyTypeOptions, minimumActivePeriodFrequencyTypeOptions, data.currentMarketPrice);
|
productOptions, chargeOptions, clientSavingsAccounts, lockinPeriodFrequencyTypeOptions,
|
||||||
|
minimumActivePeriodFrequencyTypeOptions, data.currentMarketPrice);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ShareAccountData lookup(final Long id, final String accountNo, final Long clientId, final String clientName) {
|
public static ShareAccountData lookup(final Long id, final String accountNo, final Long clientId, final String clientName) {
|
||||||
String externalId = null;
|
String externalId = null;
|
||||||
|
String resolutionNumber = null;
|
||||||
ShareAccountSummaryData summaryData = null;
|
ShareAccountSummaryData summaryData = null;
|
||||||
Collection<ShareAccountTransactionData> purchasedSharesData = null;
|
Collection<ShareAccountTransactionData> purchasedSharesData = null;
|
||||||
GLAccountData suspenseAccount = null;
|
GLAccountData suspenseAccount = null;
|
||||||
@@ -282,10 +290,11 @@ public class ShareAccountData implements Serializable, AccountData {
|
|||||||
final Long productId = null;
|
final Long productId = null;
|
||||||
final Collection<ProductData> productOptions = null;
|
final Collection<ProductData> productOptions = null;
|
||||||
|
|
||||||
return new ShareAccountData(id, accountNo, externalId, savingsAccountId, savingsAccountNumber, clientId, clientName, productId,
|
return new ShareAccountData(id, accountNo, externalId, resolutionNumber, savingsAccountId, savingsAccountNumber, clientId,
|
||||||
productName, status, timeline, currency, summaryData, charges, purchasedSharesData, lockinPeriod, lockPeriodTypeEnum,
|
clientName, productId, productName, status, timeline, currency, summaryData, charges, purchasedSharesData, lockinPeriod,
|
||||||
minimumActivePeriod, minimumActivePeriodTypeEnum, allowDividendCalculationForInactiveClients, productOptions, chargeOptions,
|
lockPeriodTypeEnum, minimumActivePeriod, minimumActivePeriodTypeEnum, allowDividendCalculationForInactiveClients,
|
||||||
clientSavingsAccounts, lockinPeriodFrequencyTypeOptions, minimumActivePeriodFrequencyTypeOption, currenMarketPrice);
|
productOptions, chargeOptions, clientSavingsAccounts, lockinPeriodFrequencyTypeOptions,
|
||||||
|
minimumActivePeriodFrequencyTypeOption, currenMarketPrice);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCurrentMarketPrice(final BigDecimal currentMarketPrice) {
|
public void setCurrentMarketPrice(final BigDecimal currentMarketPrice) {
|
||||||
|
|||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* 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.shareaccounts.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read-side view of a single pending Share Account "details" edit (m_share_account_pending_edit), for the Approvals
|
||||||
|
* -> Pending Share Account Edits screen.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class ShareAccountPendingEditData implements Serializable {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private Long shareAccountId;
|
||||||
|
private String accountNo;
|
||||||
|
private Long clientId;
|
||||||
|
private String clientName;
|
||||||
|
private Long makerId;
|
||||||
|
private String makerUsername;
|
||||||
|
private Long checkerId;
|
||||||
|
private String checkerUsername;
|
||||||
|
private LocalDateTime createdOn;
|
||||||
|
private LocalDateTime checkedOn;
|
||||||
|
private String status;
|
||||||
|
private String rejectReason;
|
||||||
|
|
||||||
|
private Long savingsAccountId;
|
||||||
|
private String savingsAccountNo;
|
||||||
|
private Boolean allowDividendCalculationForInactiveClients;
|
||||||
|
private Integer lockinPeriodFrequency;
|
||||||
|
private Integer lockinPeriodFrequencyType;
|
||||||
|
private Integer minimumActivePeriodFrequency;
|
||||||
|
private Integer minimumActivePeriodFrequencyType;
|
||||||
|
|
||||||
|
public ShareAccountPendingEditData() {}
|
||||||
|
}
|
||||||
+38
-4
@@ -105,6 +105,9 @@ public class ShareAccount extends AbstractPersistableCustom<Long> {
|
|||||||
@Column(name = "external_id")
|
@Column(name = "external_id")
|
||||||
private String externalId;
|
private String externalId;
|
||||||
|
|
||||||
|
@Column(name = "resolution_number", nullable = false)
|
||||||
|
private String resolutionNumber;
|
||||||
|
|
||||||
@Column(name = "account_no", length = 20, unique = true, nullable = false)
|
@Column(name = "account_no", length = 20, unique = true, nullable = false)
|
||||||
private String accountNumber;
|
private String accountNumber;
|
||||||
|
|
||||||
@@ -151,10 +154,11 @@ public class ShareAccount extends AbstractPersistableCustom<Long> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShareAccount(final Client client, final ShareProduct shareProduct, final String externalId, final MonetaryCurrency currency,
|
public ShareAccount(final Client client, final ShareProduct shareProduct, final String externalId, final String resolutionNumber,
|
||||||
final SavingsAccount savingsAccount, final String accountNo, final Long totalSharesApproved, final Long totalSharesPending,
|
final MonetaryCurrency currency, final SavingsAccount savingsAccount, final String accountNo,
|
||||||
final Set<ShareAccountTransaction> purchasedShares, final Boolean allowDividendCalculationForInactiveClients,
|
final Long totalSharesApproved, final Long totalSharesPending, final Set<ShareAccountTransaction> purchasedShares,
|
||||||
final Integer lockinPeriodFrequency, final PeriodFrequencyType lockPeriodType, final Integer minimumActivePeriodFrequency,
|
final Boolean allowDividendCalculationForInactiveClients, final Integer lockinPeriodFrequency,
|
||||||
|
final PeriodFrequencyType lockPeriodType, final Integer minimumActivePeriodFrequency,
|
||||||
final PeriodFrequencyType minimumActivePeriodType, Set<ShareAccountCharge> charges, AppUser submittedBy,
|
final PeriodFrequencyType minimumActivePeriodType, Set<ShareAccountCharge> charges, AppUser submittedBy,
|
||||||
final LocalDate submittedDate, AppUser approvedBy, LocalDate approvedDate, AppUser rejectedBy, LocalDate rejectedDate,
|
final LocalDate submittedDate, AppUser approvedBy, LocalDate approvedDate, AppUser rejectedBy, LocalDate rejectedDate,
|
||||||
AppUser activatedBy, LocalDate activatedDate, AppUser closedBy, LocalDate closedDate, AppUser modifiedBy,
|
AppUser activatedBy, LocalDate activatedDate, AppUser closedBy, LocalDate closedDate, AppUser modifiedBy,
|
||||||
@@ -163,6 +167,7 @@ public class ShareAccount extends AbstractPersistableCustom<Long> {
|
|||||||
this.client = client;
|
this.client = client;
|
||||||
this.shareProduct = shareProduct;
|
this.shareProduct = shareProduct;
|
||||||
this.externalId = externalId;
|
this.externalId = externalId;
|
||||||
|
this.resolutionNumber = resolutionNumber;
|
||||||
this.currency = currency;
|
this.currency = currency;
|
||||||
this.savingsAccount = savingsAccount;
|
this.savingsAccount = savingsAccount;
|
||||||
if (StringUtils.isBlank(accountNo)) {
|
if (StringUtils.isBlank(accountNo)) {
|
||||||
@@ -235,6 +240,19 @@ public class ShareAccount extends AbstractPersistableCustom<Long> {
|
|||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getResolutionNumber() {
|
||||||
|
return this.resolutionNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setResolutionNumber(final String resolutionNumber) {
|
||||||
|
boolean toReturn = false;
|
||||||
|
if (this.resolutionNumber == null || !this.resolutionNumber.equals(resolutionNumber)) {
|
||||||
|
this.resolutionNumber = resolutionNumber;
|
||||||
|
toReturn = true;
|
||||||
|
}
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean setSavingsAccount(final SavingsAccount savingsAccount) {
|
public boolean setSavingsAccount(final SavingsAccount savingsAccount) {
|
||||||
boolean returnValue = false;
|
boolean returnValue = false;
|
||||||
if (!this.savingsAccount.getId().equals(savingsAccount.getId())) {
|
if (!this.savingsAccount.getId().equals(savingsAccount.getId())) {
|
||||||
@@ -561,6 +579,22 @@ public class ShareAccount extends AbstractPersistableCustom<Long> {
|
|||||||
return this.lockinPeriodFrequencyType;
|
return this.lockinPeriodFrequencyType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Integer getMinimumActivePeriodFrequency() {
|
||||||
|
return this.minimumActivePeriodFrequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PeriodFrequencyType getMinimumActivePeriodFrequencyType() {
|
||||||
|
return this.minimumActivePeriodFrequencyType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getAllowDividendCalculationForInactiveClients() {
|
||||||
|
return this.allowDividendCalculationForInactiveClients;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SavingsAccount getSavingsAccount() {
|
||||||
|
return this.savingsAccount;
|
||||||
|
}
|
||||||
|
|
||||||
public LocalDate getActivatedDate() {
|
public LocalDate getActivatedDate() {
|
||||||
return this.activatedDate;
|
return this.activatedDate;
|
||||||
}
|
}
|
||||||
|
|||||||
+138
@@ -0,0 +1,138 @@
|
|||||||
|
/**
|
||||||
|
* 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.shareaccounts.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.EnumType;
|
||||||
|
import jakarta.persistence.Enumerated;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||||
|
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||||
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds a pending edit request for a Share Account's "details" fields (linked savings account,
|
||||||
|
* dividend-calculation-for-inactive-clients, lockin period, minimum active period). Presence of a row here with
|
||||||
|
* {@code status = PENDING} (enforced via the unique {@link #pendingLockKey}) is itself the lock - nothing is applied
|
||||||
|
* to the real {@code m_share_account} row until a checker approves (see
|
||||||
|
* ShareAccountEditWritePlatformServiceJpaRepositoryImpl).
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_share_account_pending_edit")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class ShareAccountPendingEdit extends AbstractPersistableCustom<Long> {
|
||||||
|
|
||||||
|
public static final String STATUS_PENDING = "PENDING";
|
||||||
|
public static final String STATUS_APPROVED = "APPROVED";
|
||||||
|
public static final String STATUS_REJECTED = "REJECTED";
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "share_account_id", nullable = false)
|
||||||
|
private ShareAccount shareAccount;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "createdby_id", nullable = false)
|
||||||
|
private AppUser maker;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "lastmodifiedby_id")
|
||||||
|
private AppUser checker;
|
||||||
|
|
||||||
|
@Column(name = "created_date", nullable = false)
|
||||||
|
private LocalDateTime createdOn;
|
||||||
|
|
||||||
|
@Column(name = "lastmodified_date")
|
||||||
|
private LocalDateTime checkedOn;
|
||||||
|
|
||||||
|
@Column(name = "status", nullable = false, length = 20)
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@Column(name = "reject_reason", length = 500)
|
||||||
|
private String rejectReason;
|
||||||
|
|
||||||
|
/** "SHARE_ACCOUNT_EDIT:<shareAccountId>" while PENDING, NULL otherwise - the real DB-level lock. */
|
||||||
|
@Column(name = "pending_lock_key", length = 60)
|
||||||
|
private String pendingLockKey;
|
||||||
|
|
||||||
|
/** Links to the audit-only m_portfolio_command_source row created for this edit's submit/approve/reject. */
|
||||||
|
@Column(name = "command_source_id")
|
||||||
|
private Long commandSourceId;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "savings_account_id")
|
||||||
|
private SavingsAccount savingsAccount;
|
||||||
|
|
||||||
|
@Column(name = "allow_dividends_inactive_clients")
|
||||||
|
private Boolean allowDividendCalculationForInactiveClients;
|
||||||
|
|
||||||
|
@Column(name = "lockin_period_frequency")
|
||||||
|
private Integer lockinPeriodFrequency;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.ORDINAL)
|
||||||
|
@Column(name = "lockin_period_frequency_enum")
|
||||||
|
private PeriodFrequencyType lockinPeriodFrequencyType;
|
||||||
|
|
||||||
|
@Column(name = "minimum_active_period_frequency")
|
||||||
|
private Integer minimumActivePeriodFrequency;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.ORDINAL)
|
||||||
|
@Column(name = "minimum_active_period_frequency_enum")
|
||||||
|
private PeriodFrequencyType minimumActivePeriodFrequencyType;
|
||||||
|
|
||||||
|
protected ShareAccountPendingEdit() {}
|
||||||
|
|
||||||
|
public ShareAccountPendingEdit(final ShareAccount shareAccount, final AppUser maker) {
|
||||||
|
this.shareAccount = shareAccount;
|
||||||
|
this.maker = maker;
|
||||||
|
this.createdOn = LocalDateTime.now();
|
||||||
|
this.status = STATUS_PENDING;
|
||||||
|
this.pendingLockKey = lockKeyFor(shareAccount.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String lockKeyFor(final Long shareAccountId) {
|
||||||
|
return "SHARE_ACCOUNT_EDIT:" + shareAccountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPending() {
|
||||||
|
return STATUS_PENDING.equals(this.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void markApproved(final AppUser checker) {
|
||||||
|
this.status = STATUS_APPROVED;
|
||||||
|
this.checker = checker;
|
||||||
|
this.checkedOn = LocalDateTime.now();
|
||||||
|
this.pendingLockKey = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void markRejected(final AppUser checker, final String reason) {
|
||||||
|
this.status = STATUS_REJECTED;
|
||||||
|
this.checker = checker;
|
||||||
|
this.checkedOn = LocalDateTime.now();
|
||||||
|
this.rejectReason = reason;
|
||||||
|
this.pendingLockKey = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* 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.shareaccounts.domain;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface ShareAccountPendingEditRepository extends JpaRepository<ShareAccountPendingEdit, Long> {
|
||||||
|
|
||||||
|
Optional<ShareAccountPendingEdit> findByShareAccountIdAndStatus(Long shareAccountId, String status);
|
||||||
|
|
||||||
|
List<ShareAccountPendingEdit> findByStatus(String status);
|
||||||
|
}
|
||||||
+29
@@ -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.shareaccounts.exception;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||||
|
|
||||||
|
public class ShareAccountEditPendingApprovalException extends AbstractPlatformDomainRuleException {
|
||||||
|
|
||||||
|
public ShareAccountEditPendingApprovalException(final Long shareAccountId) {
|
||||||
|
super("error.msg.shareaccount.edit.pending.approval",
|
||||||
|
"Share account with identifier " + shareAccountId + " already has a details edit pending approval", shareAccountId);
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
@@ -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.shareaccounts.exception;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||||
|
|
||||||
|
public class ShareAccountEditSelfApprovalNotAllowedException extends AbstractPlatformDomainRuleException {
|
||||||
|
|
||||||
|
public ShareAccountEditSelfApprovalNotAllowedException(final Long shareAccountId) {
|
||||||
|
super("error.msg.shareaccount.edit.self.approval.not.allowed",
|
||||||
|
"The maker of the pending details edit for share account " + shareAccountId + " cannot also approve or reject it",
|
||||||
|
shareAccountId);
|
||||||
|
}
|
||||||
|
}
|
||||||
+29
@@ -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.shareaccounts.exception;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||||
|
|
||||||
|
public class ShareAccountNoPendingEditException extends AbstractPlatformDomainRuleException {
|
||||||
|
|
||||||
|
public ShareAccountNoPendingEditException(final Long shareAccountId) {
|
||||||
|
super("error.msg.shareaccount.no.pending.edit",
|
||||||
|
"Share account with identifier " + shareAccountId + " has no details edit awaiting approval", shareAccountId);
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
-4
@@ -145,6 +145,10 @@ public class ShareAccountDataSerializer {
|
|||||||
final String externalId = this.fromApiJsonHelper.extractStringNamed(ShareAccountApiConstants.externalid_paramname, element);
|
final String externalId = this.fromApiJsonHelper.extractStringNamed(ShareAccountApiConstants.externalid_paramname, element);
|
||||||
// baseDataValidator.reset().parameter(ShareAccountApiConstants.externalid_paramname).value(externalId).notNull();
|
// baseDataValidator.reset().parameter(ShareAccountApiConstants.externalid_paramname).value(externalId).notNull();
|
||||||
|
|
||||||
|
final String resolutionNumber = this.fromApiJsonHelper.extractStringNamed(ShareAccountApiConstants.resolutionnumber_paramname,
|
||||||
|
element);
|
||||||
|
baseDataValidator.reset().parameter(ShareAccountApiConstants.resolutionnumber_paramname).value(resolutionNumber).notBlank();
|
||||||
|
|
||||||
Long savingsAccountId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.savingsaccountid_paramname, element);
|
Long savingsAccountId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.savingsaccountid_paramname, element);
|
||||||
baseDataValidator.reset().parameter(ShareAccountApiConstants.savingsaccountid_paramname).value(savingsAccountId).notNull()
|
baseDataValidator.reset().parameter(ShareAccountApiConstants.savingsaccountid_paramname).value(savingsAccountId).notNull()
|
||||||
.longGreaterThanZero();
|
.longGreaterThanZero();
|
||||||
@@ -216,10 +220,10 @@ public class ShareAccountDataSerializer {
|
|||||||
Set<ShareAccountTransaction> sharesPurchased = new HashSet<>();
|
Set<ShareAccountTransaction> sharesPurchased = new HashSet<>();
|
||||||
sharesPurchased.add(transaction);
|
sharesPurchased.add(transaction);
|
||||||
|
|
||||||
ShareAccount account = new ShareAccount(client, shareProduct, externalId, currency, savingsAccount, accountNo, approvedShares,
|
ShareAccount account = new ShareAccount(client, shareProduct, externalId, resolutionNumber, currency, savingsAccount, accountNo,
|
||||||
pendingShares, sharesPurchased, allowdividendsForInactiveClients, lockinPeriod, lockPeriodEnum, minimumActivePeriod,
|
approvedShares, pendingShares, sharesPurchased, allowdividendsForInactiveClients, lockinPeriod, lockPeriodEnum,
|
||||||
minimumActivePeriodEnum, charges, submittedBy, submittedDate, approvedBy, approvedDate, rejectedBy, rejectedDate,
|
minimumActivePeriod, minimumActivePeriodEnum, charges, submittedBy, submittedDate, approvedBy, approvedDate, rejectedBy,
|
||||||
activatedBy, activatedDate, closedBy, closedDate, modifiedBy, modifiedDate);
|
rejectedDate, activatedBy, activatedDate, closedBy, closedDate, modifiedBy, modifiedDate);
|
||||||
|
|
||||||
for (ShareAccountTransaction pur : sharesPurchased) {
|
for (ShareAccountTransaction pur : sharesPurchased) {
|
||||||
pur.setShareAccount(account);
|
pur.setShareAccount(account);
|
||||||
@@ -300,6 +304,15 @@ public class ShareAccountDataSerializer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.resolutionnumber_paramname, element)) {
|
||||||
|
final String resolutionNumber = this.fromApiJsonHelper
|
||||||
|
.extractStringNamed(ShareAccountApiConstants.resolutionnumber_paramname, element);
|
||||||
|
baseDataValidator.reset().parameter(ShareAccountApiConstants.resolutionnumber_paramname).value(resolutionNumber).notBlank();
|
||||||
|
if (account.setResolutionNumber(resolutionNumber)) {
|
||||||
|
actualChanges.put(ShareAccountApiConstants.resolutionnumber_paramname, resolutionNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.savingsaccountid_paramname, element)) {
|
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.savingsaccountid_paramname, element)) {
|
||||||
Long savingsAccountId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.savingsaccountid_paramname, element);
|
Long savingsAccountId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.savingsaccountid_paramname, element);
|
||||||
baseDataValidator.reset().parameter(ShareAccountApiConstants.savingsaccountid_paramname).value(savingsAccountId).notNull()
|
baseDataValidator.reset().parameter(ShareAccountApiConstants.savingsaccountid_paramname).value(savingsAccountId).notNull()
|
||||||
|
|||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* 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.shareaccounts.service;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountPendingEditData;
|
||||||
|
|
||||||
|
public interface ShareAccountEditWritePlatformService {
|
||||||
|
|
||||||
|
CommandProcessingResult submitEditForApproval(Long shareAccountId, JsonCommand command);
|
||||||
|
|
||||||
|
CommandProcessingResult approveEdit(Long shareAccountId);
|
||||||
|
|
||||||
|
CommandProcessingResult rejectEdit(Long shareAccountId, String rejectReason);
|
||||||
|
|
||||||
|
ShareAccountPendingEditData retrievePendingEdit(Long shareAccountId);
|
||||||
|
|
||||||
|
Collection<ShareAccountPendingEditData> retrieveAllPendingEdits();
|
||||||
|
}
|
||||||
+302
@@ -0,0 +1,302 @@
|
|||||||
|
/**
|
||||||
|
* 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.shareaccounts.service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.commands.domain.CommandProcessingResultType;
|
||||||
|
import org.apache.fineract.commands.domain.CommandSource;
|
||||||
|
import org.apache.fineract.commands.domain.CommandSourceRepository;
|
||||||
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.apache.fineract.portfolio.accounts.constants.ShareAccountApiConstants;
|
||||||
|
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||||
|
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrapper;
|
||||||
|
import org.apache.fineract.portfolio.savings.exception.SavingsAccountNotFoundException;
|
||||||
|
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
|
||||||
|
import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountPendingEditData;
|
||||||
|
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccount;
|
||||||
|
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountPendingEdit;
|
||||||
|
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountPendingEditRepository;
|
||||||
|
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountRepositoryWrapper;
|
||||||
|
import org.apache.fineract.portfolio.shareaccounts.exception.ShareAccountEditPendingApprovalException;
|
||||||
|
import org.apache.fineract.portfolio.shareaccounts.exception.ShareAccountEditSelfApprovalNotAllowedException;
|
||||||
|
import org.apache.fineract.portfolio.shareaccounts.exception.ShareAccountNoPendingEditException;
|
||||||
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maker-checker edit flow for a Share Account's "details" fields (linked savings account,
|
||||||
|
* allow-dividend-calculation-for-inactive-clients, lockin period, minimum active period). Mirrors
|
||||||
|
* {@code SavingsAccountEditWritePlatformServiceJpaRepositoryImpl}'s submitEditForApproval/approveEdit/rejectEdit
|
||||||
|
* pattern: a row in {@code m_share_account_pending_edit} is itself the lock, and nothing is applied to the real
|
||||||
|
* {@code m_share_account} row until a checker approves. Unlike the Savings Account version, this edit carries no
|
||||||
|
* balance/hold implications, so the maker's full intended new state of the 6 fields is captured (falling back to the
|
||||||
|
* account's current value for any field the maker didn't touch) rather than a partial toggle diff.
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ShareAccountEditWritePlatformServiceJpaRepositoryImpl implements ShareAccountEditWritePlatformService {
|
||||||
|
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
private final ShareAccountRepositoryWrapper shareAccountRepositoryWrapper;
|
||||||
|
private final ShareAccountPendingEditRepository pendingEditRepository;
|
||||||
|
private final SavingsAccountRepositoryWrapper savingsAccountRepositoryWrapper;
|
||||||
|
private final SavingsAccountReadPlatformService savingsAccountReadPlatformService;
|
||||||
|
private final CommandSourceRepository commandSourceRepository;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult submitEditForApproval(final Long shareAccountId, final JsonCommand command) {
|
||||||
|
|
||||||
|
final AppUser maker = this.context.authenticatedUser();
|
||||||
|
final ShareAccount account = this.shareAccountRepositoryWrapper.findOneWithNotFoundDetection(shareAccountId);
|
||||||
|
|
||||||
|
if (this.pendingEditRepository.findByShareAccountIdAndStatus(shareAccountId, ShareAccountPendingEdit.STATUS_PENDING)
|
||||||
|
.isPresent()) {
|
||||||
|
throw new ShareAccountEditPendingApprovalException(shareAccountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||||
|
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("sharesaccount");
|
||||||
|
|
||||||
|
final ShareAccountPendingEdit pendingEdit = new ShareAccountPendingEdit(account, maker);
|
||||||
|
|
||||||
|
if (command.hasParameter(ShareAccountApiConstants.savingsaccountid_paramname)) {
|
||||||
|
final Long savingsAccountId = command.longValueOfParameterNamed(ShareAccountApiConstants.savingsaccountid_paramname);
|
||||||
|
baseDataValidator.reset().parameter(ShareAccountApiConstants.savingsaccountid_paramname).value(savingsAccountId).notNull()
|
||||||
|
.longGreaterThanZero();
|
||||||
|
if (!this.savingsAccountReadPlatformService.isAccountBelongsToClient(account.getClientId(), savingsAccountId,
|
||||||
|
DepositAccountType.SAVINGS_DEPOSIT, account.getShareProduct().getCurrency().getCode())) {
|
||||||
|
throw new SavingsAccountNotFoundException(savingsAccountId);
|
||||||
|
}
|
||||||
|
pendingEdit.setSavingsAccount(this.savingsAccountRepositoryWrapper.findOneWithNotFoundDetection(savingsAccountId));
|
||||||
|
} else {
|
||||||
|
pendingEdit.setSavingsAccount(account.getSavingsAccount());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.hasParameter(ShareAccountApiConstants.allowdividendcalculationforinactiveclients_paramname)) {
|
||||||
|
pendingEdit.setAllowDividendCalculationForInactiveClients(
|
||||||
|
command.booleanObjectValueOfParameterNamed(ShareAccountApiConstants.allowdividendcalculationforinactiveclients_paramname));
|
||||||
|
} else {
|
||||||
|
pendingEdit.setAllowDividendCalculationForInactiveClients(account.getAllowDividendCalculationForInactiveClients());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.hasParameter(ShareAccountApiConstants.lockinperiod_paramname)) {
|
||||||
|
final Integer lockinPeriod = command.integerValueOfParameterNamed(ShareAccountApiConstants.lockinperiod_paramname);
|
||||||
|
baseDataValidator.reset().parameter(ShareAccountApiConstants.lockinperiod_paramname).value(lockinPeriod).notNull();
|
||||||
|
pendingEdit.setLockinPeriodFrequency(lockinPeriod);
|
||||||
|
} else {
|
||||||
|
pendingEdit.setLockinPeriodFrequency(account.getLockinPeriodFrequency());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.hasParameter(ShareAccountApiConstants.lockperiodfrequencytype_paramname)) {
|
||||||
|
pendingEdit.setLockinPeriodFrequencyType(
|
||||||
|
extractPeriodType(command.integerValueOfParameterNamed(ShareAccountApiConstants.lockperiodfrequencytype_paramname)));
|
||||||
|
} else {
|
||||||
|
pendingEdit.setLockinPeriodFrequencyType(account.getLockinPeriodFrequencyType());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.hasParameter(ShareAccountApiConstants.minimumactiveperiod_paramname)) {
|
||||||
|
final Integer minimumActivePeriod = command.integerValueOfParameterNamed(ShareAccountApiConstants.minimumactiveperiod_paramname);
|
||||||
|
baseDataValidator.reset().parameter(ShareAccountApiConstants.minimumactiveperiod_paramname).value(minimumActivePeriod)
|
||||||
|
.notNull();
|
||||||
|
pendingEdit.setMinimumActivePeriodFrequency(minimumActivePeriod);
|
||||||
|
} else {
|
||||||
|
pendingEdit.setMinimumActivePeriodFrequency(account.getMinimumActivePeriodFrequency());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.hasParameter(ShareAccountApiConstants.minimumactiveperiodfrequencytype_paramname)) {
|
||||||
|
pendingEdit.setMinimumActivePeriodFrequencyType(extractPeriodType(
|
||||||
|
command.integerValueOfParameterNamed(ShareAccountApiConstants.minimumactiveperiodfrequencytype_paramname)));
|
||||||
|
} else {
|
||||||
|
pendingEdit.setMinimumActivePeriodFrequencyType(account.getMinimumActivePeriodFrequencyType());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dataValidationErrors.isEmpty()) {
|
||||||
|
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
final CommandSource auditEntry = CommandSource.builder() //
|
||||||
|
.actionName("SUBMITEDIT") //
|
||||||
|
.entityName("SHAREACCOUNT") //
|
||||||
|
.resourceGetUrl("/shareaccounts/" + shareAccountId) //
|
||||||
|
.resourceId(shareAccountId) //
|
||||||
|
.commandAsJson(command.json()) //
|
||||||
|
.maker(maker) //
|
||||||
|
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||||
|
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||||
|
.idempotencyKey(UUID.randomUUID().toString()) //
|
||||||
|
.sanitized(false) //
|
||||||
|
.build();
|
||||||
|
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||||
|
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||||
|
|
||||||
|
this.pendingEditRepository.saveAndFlush(pendingEdit);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder().withEntityId(shareAccountId).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult approveEdit(final Long shareAccountId) {
|
||||||
|
|
||||||
|
final AppUser checker = this.context.authenticatedUser();
|
||||||
|
final ShareAccountPendingEdit pendingEdit = this.pendingEditRepository
|
||||||
|
.findByShareAccountIdAndStatus(shareAccountId, ShareAccountPendingEdit.STATUS_PENDING)
|
||||||
|
.orElseThrow(() -> new ShareAccountNoPendingEditException(shareAccountId));
|
||||||
|
|
||||||
|
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||||
|
throw new ShareAccountEditSelfApprovalNotAllowedException(shareAccountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final ShareAccount account = this.shareAccountRepositoryWrapper.findOneWithNotFoundDetection(shareAccountId);
|
||||||
|
|
||||||
|
if (pendingEdit.getSavingsAccount() != null) {
|
||||||
|
account.setSavingsAccount(pendingEdit.getSavingsAccount());
|
||||||
|
}
|
||||||
|
account.setAllowDividendCalculationForInactiveClients(pendingEdit.getAllowDividendCalculationForInactiveClients());
|
||||||
|
if (pendingEdit.getLockinPeriodFrequency() != null) {
|
||||||
|
account.setLockPeriod(pendingEdit.getLockinPeriodFrequency());
|
||||||
|
}
|
||||||
|
if (pendingEdit.getLockinPeriodFrequencyType() != null) {
|
||||||
|
account.setLockPeriodFrequencyEnum(pendingEdit.getLockinPeriodFrequencyType());
|
||||||
|
}
|
||||||
|
if (pendingEdit.getMinimumActivePeriodFrequency() != null) {
|
||||||
|
account.setminimumActivePeriod(pendingEdit.getMinimumActivePeriodFrequency());
|
||||||
|
}
|
||||||
|
if (pendingEdit.getMinimumActivePeriodFrequencyType() != null) {
|
||||||
|
account.setminimumActivePeriodTypeEnum(pendingEdit.getMinimumActivePeriodFrequencyType());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.shareAccountRepositoryWrapper.saveAndFlush(account);
|
||||||
|
|
||||||
|
pendingEdit.markApproved(checker);
|
||||||
|
this.pendingEditRepository.saveAndFlush(pendingEdit);
|
||||||
|
|
||||||
|
updateAuditEntry(pendingEdit, checker, false);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder().withEntityId(shareAccountId).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult rejectEdit(final Long shareAccountId, final String rejectReason) {
|
||||||
|
|
||||||
|
final AppUser checker = this.context.authenticatedUser();
|
||||||
|
final ShareAccountPendingEdit pendingEdit = this.pendingEditRepository
|
||||||
|
.findByShareAccountIdAndStatus(shareAccountId, ShareAccountPendingEdit.STATUS_PENDING)
|
||||||
|
.orElseThrow(() -> new ShareAccountNoPendingEditException(shareAccountId));
|
||||||
|
|
||||||
|
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||||
|
throw new ShareAccountEditSelfApprovalNotAllowedException(shareAccountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingEdit.markRejected(checker, rejectReason);
|
||||||
|
this.pendingEditRepository.saveAndFlush(pendingEdit);
|
||||||
|
|
||||||
|
updateAuditEntry(pendingEdit, checker, true);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder().withEntityId(shareAccountId).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ShareAccountPendingEditData retrievePendingEdit(final Long shareAccountId) {
|
||||||
|
return this.pendingEditRepository.findByShareAccountIdAndStatus(shareAccountId, ShareAccountPendingEdit.STATUS_PENDING)
|
||||||
|
.map(this::toData).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ShareAccountPendingEditData> retrieveAllPendingEdits() {
|
||||||
|
return this.pendingEditRepository.findByStatus(ShareAccountPendingEdit.STATUS_PENDING).stream().map(this::toData).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── helpers ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private PeriodFrequencyType extractPeriodType(final Integer value) {
|
||||||
|
return value == null ? null : PeriodFrequencyType.fromInt(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAuditEntry(final ShareAccountPendingEdit pendingEdit, final AppUser checker, final boolean rejected) {
|
||||||
|
Optional<CommandSource> existing = Optional.empty();
|
||||||
|
if (pendingEdit.getCommandSourceId() != null) {
|
||||||
|
existing = this.commandSourceRepository.findById(pendingEdit.getCommandSourceId());
|
||||||
|
}
|
||||||
|
final CommandSource auditEntry = existing.orElseGet(() -> CommandSource.builder() //
|
||||||
|
.actionName(rejected ? "REJECTEDIT" : "APPROVEEDIT") //
|
||||||
|
.entityName("SHAREACCOUNT") //
|
||||||
|
.resourceGetUrl("/shareaccounts/" + pendingEdit.getShareAccount().getId()) //
|
||||||
|
.resourceId(pendingEdit.getShareAccount().getId()) //
|
||||||
|
.maker(pendingEdit.getMaker()) //
|
||||||
|
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||||
|
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||||
|
.idempotencyKey(UUID.randomUUID().toString()) //
|
||||||
|
.sanitized(false) //
|
||||||
|
.build());
|
||||||
|
if (rejected) {
|
||||||
|
auditEntry.markAsRejected(checker);
|
||||||
|
} else {
|
||||||
|
auditEntry.markAsChecked(checker);
|
||||||
|
}
|
||||||
|
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ShareAccountPendingEditData toData(final ShareAccountPendingEdit p) {
|
||||||
|
final ShareAccountPendingEditData data = new ShareAccountPendingEditData();
|
||||||
|
data.setId(p.getId());
|
||||||
|
data.setShareAccountId(p.getShareAccount().getId());
|
||||||
|
data.setAccountNo(p.getShareAccount().getAccountNumber());
|
||||||
|
data.setClientId(p.getShareAccount().getClientId());
|
||||||
|
data.setMakerId(p.getMaker().getId());
|
||||||
|
data.setMakerUsername(p.getMaker().getUsername());
|
||||||
|
if (p.getChecker() != null) {
|
||||||
|
data.setCheckerId(p.getChecker().getId());
|
||||||
|
data.setCheckerUsername(p.getChecker().getUsername());
|
||||||
|
}
|
||||||
|
data.setCreatedOn(p.getCreatedOn());
|
||||||
|
data.setCheckedOn(p.getCheckedOn());
|
||||||
|
data.setStatus(p.getStatus());
|
||||||
|
data.setRejectReason(p.getRejectReason());
|
||||||
|
if (p.getSavingsAccount() != null) {
|
||||||
|
data.setSavingsAccountId(p.getSavingsAccount().getId());
|
||||||
|
data.setSavingsAccountNo(p.getSavingsAccount().getAccountNumber());
|
||||||
|
}
|
||||||
|
data.setAllowDividendCalculationForInactiveClients(p.getAllowDividendCalculationForInactiveClients());
|
||||||
|
data.setLockinPeriodFrequency(p.getLockinPeriodFrequency());
|
||||||
|
if (p.getLockinPeriodFrequencyType() != null) {
|
||||||
|
data.setLockinPeriodFrequencyType(p.getLockinPeriodFrequencyType().getValue());
|
||||||
|
}
|
||||||
|
data.setMinimumActivePeriodFrequency(p.getMinimumActivePeriodFrequency());
|
||||||
|
if (p.getMinimumActivePeriodFrequencyType() != null) {
|
||||||
|
data.setMinimumActivePeriodFrequencyType(p.getMinimumActivePeriodFrequencyType().getValue());
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
-7
@@ -241,7 +241,8 @@ public class ShareAccountReadPlatformServiceImpl implements ShareAccountReadPlat
|
|||||||
final Collection<ShareAccountTransactionData> purchasedShares) {
|
final Collection<ShareAccountTransactionData> purchasedShares) {
|
||||||
this.charges = charges;
|
this.charges = charges;
|
||||||
this.purchasedShares = purchasedShares;
|
this.purchasedShares = purchasedShares;
|
||||||
StringBuilder buff = new StringBuilder().append("sa.id as id, sa.external_id as externalId, sa.status_enum as statusEnum, ")
|
StringBuilder buff = new StringBuilder().append(
|
||||||
|
"sa.id as id, sa.external_id as externalId, sa.resolution_number as resolutionNumber, sa.status_enum as statusEnum, ")
|
||||||
.append("sa.savings_account_id, msa.account_no as savingsAccNo, ")
|
.append("sa.savings_account_id, msa.account_no as savingsAccNo, ")
|
||||||
.append("c.id as clientId, c.display_name as clientName, ")
|
.append("c.id as clientId, c.display_name as clientName, ")
|
||||||
.append("sa.account_no as accountNo, sa.total_approved_shares as approvedShares, sa.total_pending_shares as pendingShares, ")
|
.append("sa.account_no as accountNo, sa.total_approved_shares as approvedShares, sa.total_pending_shares as pendingShares, ")
|
||||||
@@ -278,6 +279,7 @@ public class ShareAccountReadPlatformServiceImpl implements ShareAccountReadPlat
|
|||||||
final Long id = rs.getLong("id");
|
final Long id = rs.getLong("id");
|
||||||
final String accountNo = rs.getString("accountNo");
|
final String accountNo = rs.getString("accountNo");
|
||||||
final String externalId = rs.getString("externalId");
|
final String externalId = rs.getString("externalId");
|
||||||
|
final String resolutionNumber = rs.getString("resolutionNumber");
|
||||||
final Long savingsAccountId = JdbcSupport.getLong(rs, "savings_account_id");
|
final Long savingsAccountId = JdbcSupport.getLong(rs, "savings_account_id");
|
||||||
final String savingsAccountNumber = rs.getString("savingsAccNo");
|
final String savingsAccountNumber = rs.getString("savingsAccNo");
|
||||||
final Long clientId = JdbcSupport.getLong(rs, "clientId");
|
final Long clientId = JdbcSupport.getLong(rs, "clientId");
|
||||||
@@ -349,9 +351,10 @@ public class ShareAccountReadPlatformServiceImpl implements ShareAccountReadPlat
|
|||||||
final String shortProductName = null;
|
final String shortProductName = null;
|
||||||
final ShareAccountSummaryData summary = new ShareAccountSummaryData(id, accountNo, externalId, productId, productName,
|
final ShareAccountSummaryData summary = new ShareAccountSummaryData(id, accountNo, externalId, productId, productName,
|
||||||
shortProductName, status, currency, totalApprovedShares, totalPendingShares, timeline);
|
shortProductName, status, currency, totalApprovedShares, totalPendingShares, timeline);
|
||||||
return new ShareAccountData(id, accountNo, externalId, savingsAccountId, savingsAccountNumber, clientId, clientName, productId,
|
return new ShareAccountData(id, accountNo, externalId, resolutionNumber, savingsAccountId, savingsAccountNumber, clientId,
|
||||||
productName, status, timeline, currency, summary, charges, purchasedShares, lockinPeriodFrequency,
|
clientName, productId, productName, status, timeline, currency, summary, charges, purchasedShares,
|
||||||
lockinPeriodFrequencyType, minimumActivePeriod, minimumActivePeriodType, allowdividendsforinactiveclients);
|
lockinPeriodFrequency, lockinPeriodFrequencyType, minimumActivePeriod, minimumActivePeriodType,
|
||||||
|
allowdividendsforinactiveclients);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,6 +395,7 @@ public class ShareAccountReadPlatformServiceImpl implements ShareAccountReadPlat
|
|||||||
final Long totalApprovedShares = null;
|
final Long totalApprovedShares = null;
|
||||||
final Long totalPendingShares = null;
|
final Long totalPendingShares = null;
|
||||||
final String externalId = null;
|
final String externalId = null;
|
||||||
|
final String resolutionNumber = null;
|
||||||
final Long productId = null;
|
final Long productId = null;
|
||||||
final String productName = null;
|
final String productName = null;
|
||||||
final String shortProductName = null;
|
final String shortProductName = null;
|
||||||
@@ -418,9 +422,9 @@ public class ShareAccountReadPlatformServiceImpl implements ShareAccountReadPlat
|
|||||||
final ShareAccountSummaryData summary = new ShareAccountSummaryData(id, accountNo, externalId, productId, productName,
|
final ShareAccountSummaryData summary = new ShareAccountSummaryData(id, accountNo, externalId, productId, productName,
|
||||||
shortProductName, status, currency, totalApprovedShares, totalPendingShares, timeline);
|
shortProductName, status, currency, totalApprovedShares, totalPendingShares, timeline);
|
||||||
|
|
||||||
return new ShareAccountData(id, accountNo, externalId, clientId, clientName, productId, shortProductName, productId,
|
return new ShareAccountData(id, accountNo, externalId, resolutionNumber, clientId, clientName, productId, shortProductName,
|
||||||
shortProductName, status, timeline, currency, summary, charges, purchasedSharesData, lockinPeriod, lockPeriodTypeEnum,
|
productId, shortProductName, status, timeline, currency, summary, charges, purchasedSharesData, lockinPeriod,
|
||||||
minimumActivePeriod, minimumActivePeriodTypeEnum, allowdividendsforinactiveclients);
|
lockPeriodTypeEnum, minimumActivePeriod, minimumActivePeriodTypeEnum, allowdividendsforinactiveclients);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+17
@@ -34,7 +34,12 @@ import org.apache.fineract.portfolio.note.domain.NoteRepository;
|
|||||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
|
||||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountDomainService;
|
import org.apache.fineract.portfolio.savings.service.SavingsAccountDomainService;
|
||||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
|
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
|
||||||
|
import org.apache.fineract.commands.domain.CommandSourceRepository;
|
||||||
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrapper;
|
||||||
|
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountDividendRepository;
|
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountDividendRepository;
|
||||||
|
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountPendingEditRepository;
|
||||||
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountRepositoryWrapper;
|
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountRepositoryWrapper;
|
||||||
import org.apache.fineract.portfolio.shareaccounts.serialization.ShareAccountDataSerializer;
|
import org.apache.fineract.portfolio.shareaccounts.serialization.ShareAccountDataSerializer;
|
||||||
import org.apache.fineract.portfolio.shareaccounts.service.PurchasedSharesReadPlatformService;
|
import org.apache.fineract.portfolio.shareaccounts.service.PurchasedSharesReadPlatformService;
|
||||||
@@ -44,6 +49,8 @@ import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountChargeRea
|
|||||||
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountCommandsServiceImpl;
|
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountCommandsServiceImpl;
|
||||||
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountDividendReadPlatformService;
|
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountDividendReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountDividendReadPlatformServiceImpl;
|
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountDividendReadPlatformServiceImpl;
|
||||||
|
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountEditWritePlatformService;
|
||||||
|
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountEditWritePlatformServiceJpaRepositoryImpl;
|
||||||
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountReadPlatformService;
|
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountReadPlatformServiceImpl;
|
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountReadPlatformServiceImpl;
|
||||||
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountSchedularService;
|
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountSchedularService;
|
||||||
@@ -121,4 +128,14 @@ public class ShareAccountsConfiguration {
|
|||||||
accountNumberGenerator, accountNumberFormatRepository, journalEntryWritePlatformService, noteRepository,
|
accountNumberGenerator, accountNumberFormatRepository, journalEntryWritePlatformService, noteRepository,
|
||||||
jdbcTemplate, businessEventNotifierService);
|
jdbcTemplate, businessEventNotifierService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(ShareAccountEditWritePlatformService.class)
|
||||||
|
public ShareAccountEditWritePlatformService shareAccountEditWritePlatformService(PlatformSecurityContext context,
|
||||||
|
ShareAccountRepositoryWrapper shareAccountRepositoryWrapper, ShareAccountPendingEditRepository pendingEditRepository,
|
||||||
|
SavingsAccountRepositoryWrapper savingsAccountRepositoryWrapper,
|
||||||
|
SavingsAccountReadPlatformService savingsAccountReadPlatformService, CommandSourceRepository commandSourceRepository) {
|
||||||
|
return new ShareAccountEditWritePlatformServiceJpaRepositoryImpl(context, shareAccountRepositoryWrapper, pendingEditRepository,
|
||||||
|
savingsAccountRepositoryWrapper, savingsAccountReadPlatformService, commandSourceRepository);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -466,4 +466,7 @@
|
|||||||
<include file="parts/0357_add_dd_limit_to_sponsor_dd_gl_mapping.xml" relativeToChangelogFile="true"/>
|
<include file="parts/0357_add_dd_limit_to_sponsor_dd_gl_mapping.xml" relativeToChangelogFile="true"/>
|
||||||
<include file="parts/0358_add_batch_id_to_sponsor_dd_issue_request.xml" relativeToChangelogFile="true"/>
|
<include file="parts/0358_add_batch_id_to_sponsor_dd_issue_request.xml" relativeToChangelogFile="true"/>
|
||||||
<include file="parts/0359_reclassify_dd_gst_payable_as_income.xml" relativeToChangelogFile="true"/>
|
<include file="parts/0359_reclassify_dd_gst_payable_as_income.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0360_add_resolution_number_to_share_account.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0361_add_share_account_pending_edit_table.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0362_add_share_account_edit_approval_permissions.xml" relativeToChangelogFile="true"/>
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||||
|
<changeSet author="fineract" id="1">
|
||||||
|
<addColumn tableName="m_share_account">
|
||||||
|
<column name="resolution_number" type="VARCHAR(100)" defaultValue="">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
+81
@@ -0,0 +1,81 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Holds a pending edit request for a Share Account's "details" fields (linked savings account,
|
||||||
|
dividend-calculation-for-inactive-clients, lockin period, minimum active period). Presence of a
|
||||||
|
row here with status=PENDING (enforced via the unique pending_lock_key) is itself the lock -
|
||||||
|
nothing is applied to the real m_share_account row until a checker approves
|
||||||
|
(see ShareAccountEditWritePlatformServiceJpaRepositoryImpl).
|
||||||
|
-->
|
||||||
|
<changeSet author="fineract" id="1">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<not>
|
||||||
|
<tableExists tableName="m_share_account_pending_edit" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<createTable tableName="m_share_account_pending_edit">
|
||||||
|
<column name="id" type="BIGINT" autoIncrement="true">
|
||||||
|
<constraints primaryKey="true" nullable="false" />
|
||||||
|
</column>
|
||||||
|
<column name="share_account_id" type="BIGINT">
|
||||||
|
<constraints nullable="false" foreignKeyName="fk_shaoe_share_account_id" references="m_share_account(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="createdby_id" type="BIGINT">
|
||||||
|
<constraints nullable="false" foreignKeyName="fk_shaoe_maker_id" references="m_appuser(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="lastmodifiedby_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" foreignKeyName="fk_shaoe_checker_id" references="m_appuser(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="created_date" type="DATETIME">
|
||||||
|
<constraints nullable="false" />
|
||||||
|
</column>
|
||||||
|
<column name="lastmodified_date" type="DATETIME">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
<column name="status" type="VARCHAR(20)" defaultValue="PENDING">
|
||||||
|
<constraints nullable="false" />
|
||||||
|
</column>
|
||||||
|
<column name="reject_reason" type="VARCHAR(500)" />
|
||||||
|
<column name="pending_lock_key" type="VARCHAR(60)">
|
||||||
|
<constraints nullable="true" unique="true" uniqueConstraintName="shaoe_unq_pending_lock_key" />
|
||||||
|
</column>
|
||||||
|
<column name="command_source_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" foreignKeyName="fk_shaoe_command_source_id" references="m_portfolio_command_source(id)" />
|
||||||
|
</column>
|
||||||
|
|
||||||
|
<!-- Proposed new values for the 6 editable "details" fields. NULL means the maker left that
|
||||||
|
field unchanged from the account's current value at submission time. -->
|
||||||
|
<column name="savings_account_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" foreignKeyName="fk_shaoe_savings_account_id" references="m_savings_account(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="allow_dividends_inactive_clients" type="BOOLEAN" />
|
||||||
|
<column name="lockin_period_frequency" type="INTEGER" />
|
||||||
|
<column name="lockin_period_frequency_enum" type="SMALLINT" />
|
||||||
|
<column name="minimum_active_period_frequency" type="INTEGER" />
|
||||||
|
<column name="minimum_active_period_frequency_enum" type="SMALLINT" />
|
||||||
|
</createTable>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
+83
@@ -0,0 +1,83 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Permissions gating the Share Account "Edit Details" maker-checker flow. SUBMITEDIT lets a maker
|
||||||
|
propose the edit (the "Edit Details" action on the account view); APPROVEEDIT/REJECTEDIT let a
|
||||||
|
checker resolve it. The _CHECKER permission follows the "<ACTION>_<ENTITY>_CHECKER" convention so
|
||||||
|
the pending request also surfaces in the generic Checker Inbox for limited checkers.
|
||||||
|
-->
|
||||||
|
<changeSet author="fineract" id="1">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'SUBMITEDIT_SHAREACCOUNT'</sqlCheck>
|
||||||
|
</preConditions>
|
||||||
|
<insert tableName="m_permission">
|
||||||
|
<column name="grouping" value="SHAREACCOUNT" />
|
||||||
|
<column name="code" value="SUBMITEDIT_SHAREACCOUNT" />
|
||||||
|
<column name="entity_name" value="SHAREACCOUNT" />
|
||||||
|
<column name="action_name" value="SUBMITEDIT" />
|
||||||
|
<column name="can_maker_checker" valueBoolean="false" />
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="2">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'APPROVEEDIT_SHAREACCOUNT'</sqlCheck>
|
||||||
|
</preConditions>
|
||||||
|
<insert tableName="m_permission">
|
||||||
|
<column name="grouping" value="SHAREACCOUNT" />
|
||||||
|
<column name="code" value="APPROVEEDIT_SHAREACCOUNT" />
|
||||||
|
<column name="entity_name" value="SHAREACCOUNT" />
|
||||||
|
<column name="action_name" value="APPROVEEDIT" />
|
||||||
|
<column name="can_maker_checker" valueBoolean="false" />
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="3">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'REJECTEDIT_SHAREACCOUNT'</sqlCheck>
|
||||||
|
</preConditions>
|
||||||
|
<insert tableName="m_permission">
|
||||||
|
<column name="grouping" value="SHAREACCOUNT" />
|
||||||
|
<column name="code" value="REJECTEDIT_SHAREACCOUNT" />
|
||||||
|
<column name="entity_name" value="SHAREACCOUNT" />
|
||||||
|
<column name="action_name" value="REJECTEDIT" />
|
||||||
|
<column name="can_maker_checker" valueBoolean="false" />
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="4">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'APPROVEEDIT_SHAREACCOUNT_CHECKER'</sqlCheck>
|
||||||
|
</preConditions>
|
||||||
|
<insert tableName="m_permission">
|
||||||
|
<column name="grouping" value="SHAREACCOUNT" />
|
||||||
|
<column name="code" value="APPROVEEDIT_SHAREACCOUNT_CHECKER" />
|
||||||
|
<column name="entity_name" value="SHAREACCOUNT" />
|
||||||
|
<column name="action_name" value="APPROVEEDIT_CHECKER" />
|
||||||
|
<column name="can_maker_checker" valueBoolean="false" />
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
+18
@@ -116,6 +116,21 @@ public final class FixedDepositAccountData extends DepositAccountData {
|
|||||||
private Boolean deceasedSettlement;
|
private Boolean deceasedSettlement;
|
||||||
private Boolean allowDebitTxnAfterDueDate;
|
private Boolean allowDebitTxnAfterDueDate;
|
||||||
|
|
||||||
|
// Live per-account dormancy state (distinct from the product-level
|
||||||
|
// isDormancyTrackingActive/daysToInactive/daysToDormancy/daysToEscheat
|
||||||
|
// thresholds above): the account's actual current sub-status and the
|
||||||
|
// computed countdown, mirroring SavingsAccountData.
|
||||||
|
private SavingsAccountSubStatusEnumData subStatus;
|
||||||
|
private String reasonForBlock;
|
||||||
|
private LocalDate lastActiveTransactionDate;
|
||||||
|
|
||||||
|
public void setDormancyStatus(final SavingsAccountSubStatusEnumData subStatus, final String reasonForBlock,
|
||||||
|
final LocalDate lastActiveTransactionDate) {
|
||||||
|
this.subStatus = subStatus;
|
||||||
|
this.reasonForBlock = reasonForBlock;
|
||||||
|
this.lastActiveTransactionDate = lastActiveTransactionDate;
|
||||||
|
}
|
||||||
|
|
||||||
// Joint holders for JOINT account status
|
// Joint holders for JOINT account status
|
||||||
private java.util.List<java.util.Map<String, Object>> jointHolders;
|
private java.util.List<java.util.Map<String, Object>> jointHolders;
|
||||||
|
|
||||||
@@ -314,6 +329,9 @@ public final class FixedDepositAccountData extends DepositAccountData {
|
|||||||
this.jointHolders = src.jointHolders;
|
this.jointHolders = src.jointHolders;
|
||||||
this.isSeniorCitizen = src.isSeniorCitizen;
|
this.isSeniorCitizen = src.isSeniorCitizen;
|
||||||
this.seniorCitizenInterestRate = src.seniorCitizenInterestRate;
|
this.seniorCitizenInterestRate = src.seniorCitizenInterestRate;
|
||||||
|
this.subStatus = src.subStatus;
|
||||||
|
this.reasonForBlock = src.reasonForBlock;
|
||||||
|
this.lastActiveTransactionDate = src.lastActiveTransactionDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
private FixedDepositAccountData(Long clientId, Long productId, Long fieldofficerId, LocalDate submittedOnDate,
|
private FixedDepositAccountData(Long clientId, Long productId, Long fieldofficerId, LocalDate submittedOnDate,
|
||||||
|
|||||||
+18
@@ -147,6 +147,21 @@ public final class RecurringDepositAccountData extends DepositAccountData {
|
|||||||
private Boolean deceasedSettlement;
|
private Boolean deceasedSettlement;
|
||||||
private Boolean allowDebitTxnAfterDueDate;
|
private Boolean allowDebitTxnAfterDueDate;
|
||||||
|
|
||||||
|
// Live per-account dormancy state (distinct from the product-level
|
||||||
|
// isDormancyTrackingActive/daysToInactive/daysToDormancy/daysToEscheat
|
||||||
|
// thresholds above): the account's actual current sub-status and the
|
||||||
|
// computed countdown, mirroring SavingsAccountData.
|
||||||
|
private SavingsAccountSubStatusEnumData subStatus;
|
||||||
|
private String reasonForBlock;
|
||||||
|
private LocalDate lastActiveTransactionDate;
|
||||||
|
|
||||||
|
public void setDormancyStatus(final SavingsAccountSubStatusEnumData subStatus, final String reasonForBlock,
|
||||||
|
final LocalDate lastActiveTransactionDate) {
|
||||||
|
this.subStatus = subStatus;
|
||||||
|
this.reasonForBlock = reasonForBlock;
|
||||||
|
this.lastActiveTransactionDate = lastActiveTransactionDate;
|
||||||
|
}
|
||||||
|
|
||||||
public void setAccountTypeOptions(Collection<CodeValueData> accountTypeOptions) {
|
public void setAccountTypeOptions(Collection<CodeValueData> accountTypeOptions) {
|
||||||
this.accountTypeOptions = accountTypeOptions;
|
this.accountTypeOptions = accountTypeOptions;
|
||||||
}
|
}
|
||||||
@@ -238,6 +253,9 @@ public final class RecurringDepositAccountData extends DepositAccountData {
|
|||||||
this.jointHolders = src.jointHolders;
|
this.jointHolders = src.jointHolders;
|
||||||
this.isSeniorCitizen = src.isSeniorCitizen;
|
this.isSeniorCitizen = src.isSeniorCitizen;
|
||||||
this.seniorCitizenInterestRate = src.seniorCitizenInterestRate;
|
this.seniorCitizenInterestRate = src.seniorCitizenInterestRate;
|
||||||
|
this.subStatus = src.subStatus;
|
||||||
|
this.reasonForBlock = src.reasonForBlock;
|
||||||
|
this.lastActiveTransactionDate = src.lastActiveTransactionDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCbsProductFields(String accountType, String customerSegment, String productCode,
|
public void setCbsProductFields(String accountType, String customerSegment, String productCode,
|
||||||
|
|||||||
+11
-4
@@ -748,10 +748,17 @@ public final class ProductPendingEditFieldMapper {
|
|||||||
f.setTotalShares(command.longValueOfParameterNamed("totalShares"));
|
f.setTotalShares(command.longValueOfParameterNamed("totalShares"));
|
||||||
present.add("totalShares");
|
present.add("totalShares");
|
||||||
}
|
}
|
||||||
if (command.hasParameter("totalSharesIssued") || command.hasParameter("issuedShares")) {
|
if (command.hasParameter("sharesIssued") || command.hasParameter("totalSharesIssued") || command.hasParameter("issuedShares")) {
|
||||||
final Long val = command.hasParameter("totalSharesIssued") ? command.longValueOfParameterNamed("totalSharesIssued") : command.longValueOfParameterNamed("issuedShares");
|
final Long val;
|
||||||
|
if (command.hasParameter("sharesIssued")) {
|
||||||
|
val = command.longValueOfParameterNamed("sharesIssued");
|
||||||
|
} else if (command.hasParameter("totalSharesIssued")) {
|
||||||
|
val = command.longValueOfParameterNamed("totalSharesIssued");
|
||||||
|
} else {
|
||||||
|
val = command.longValueOfParameterNamed("issuedShares");
|
||||||
|
}
|
||||||
f.setTotalSharesIssued(val);
|
f.setTotalSharesIssued(val);
|
||||||
present.add("totalSharesIssued");
|
present.add("sharesIssued");
|
||||||
}
|
}
|
||||||
if (command.hasParameter("unitPrice")) {
|
if (command.hasParameter("unitPrice")) {
|
||||||
f.setUnitPrice(command.bigDecimalValueOfParameterNamed("unitPrice"));
|
f.setUnitPrice(command.bigDecimalValueOfParameterNamed("unitPrice"));
|
||||||
@@ -820,7 +827,7 @@ public final class ProductPendingEditFieldMapper {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
putNumber(root, submitted, "totalShares", f.getTotalShares());
|
putNumber(root, submitted, "totalShares", f.getTotalShares());
|
||||||
putNumber(root, submitted, "totalSharesIssued", f.getTotalSharesIssued());
|
putNumber(root, submitted, "sharesIssued", f.getTotalSharesIssued());
|
||||||
putNumber(root, submitted, "unitPrice", f.getUnitPrice());
|
putNumber(root, submitted, "unitPrice", f.getUnitPrice());
|
||||||
putNumber(root, submitted, "minimumShares", f.getMinimumShares());
|
putNumber(root, submitted, "minimumShares", f.getMinimumShares());
|
||||||
putNumber(root, submitted, "nominalShares", f.getNominalShares());
|
putNumber(root, submitted, "nominalShares", f.getNominalShares());
|
||||||
|
|||||||
Reference in New Issue
Block a user