merge(release): merge feature/minor-update into release/share-account-module
Combines election module + UAT catch-up migrations (0338-0367) with minor-update changes: soft delete for tellers (0338), share linking for current account (0339), terminate flag for savings/current (0340). Resolved changelog conflict by appending minor-update entries after 0367. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+11
@@ -76,6 +76,9 @@ public class Teller extends AbstractPersistableCustom<Long> {
|
||||
@Column(name = "state", nullable = false)
|
||||
private Integer status;
|
||||
|
||||
@Column(name = "is_deleted", nullable = false)
|
||||
private boolean deleted = false;
|
||||
|
||||
@OneToMany(mappedBy = "teller", fetch = FetchType.LAZY)
|
||||
private Set<Cashier> cashiers;
|
||||
|
||||
@@ -161,4 +164,12 @@ public class Teller extends AbstractPersistableCustom<Long> {
|
||||
this.office.getId();
|
||||
this.cashiers.size();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
this.deleted = true;
|
||||
}
|
||||
|
||||
public boolean isDeleted() {
|
||||
return this.deleted;
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -31,4 +31,6 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
public interface TellerRepository extends JpaRepository<Teller, Long>, JpaSpecificationExecutor<Teller> {
|
||||
|
||||
Collection<Teller> findTellerByOfficeId(Long officeId);
|
||||
|
||||
long countByDeletedFalse();
|
||||
}
|
||||
|
||||
+7
@@ -36,6 +36,9 @@ public class TellerRepositoryWrapper {
|
||||
@Transactional(readOnly = true)
|
||||
public Teller findOneWithNotFoundDetection(final Long id) {
|
||||
final Teller teller = this.repository.findById(id).orElseThrow(() -> new TellerNotFoundException(id));
|
||||
if (teller.isDeleted()) {
|
||||
throw new TellerNotFoundException(id);
|
||||
}
|
||||
teller.initializeLazyCollections();
|
||||
return teller;
|
||||
}
|
||||
@@ -51,4 +54,8 @@ public class TellerRepositoryWrapper {
|
||||
public void delete(final Teller teller) {
|
||||
this.repository.delete(teller);
|
||||
}
|
||||
|
||||
public boolean existsAny() {
|
||||
return this.repository.countByDeletedFalse() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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.organisation.teller.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
public class TellerAlreadyExistsException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
private static final String ERROR_MESSAGE_CODE = "error.msg.teller.already.exists";
|
||||
private static final String DEFAULT_ERROR_MESSAGE = "Cannot create teller as a teller is already present.";
|
||||
|
||||
public TellerAlreadyExistsException() {
|
||||
super(ERROR_MESSAGE_CODE, DEFAULT_ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -34,7 +34,8 @@ public enum CommandProcessingResultType {
|
||||
AWAITING_APPROVAL(2, "commandProcessingResultType.awaiting.approval"), //
|
||||
REJECTED(3, "commandProcessingResultType.rejected"), //
|
||||
UNDER_PROCESSING(4, "commandProcessingResultType.underProcessing"), //
|
||||
ERROR(5, "commandProcessingResultType.error");
|
||||
ERROR(5, "commandProcessingResultType.error"), //
|
||||
TERMINATE(6, "commandProcessingResultType.terminate");
|
||||
|
||||
private static final Map<Integer, CommandProcessingResultType> BY_ID = Arrays.stream(values())
|
||||
.collect(Collectors.toMap(CommandProcessingResultType::getValue, v -> v));
|
||||
@@ -59,4 +60,8 @@ public enum CommandProcessingResultType {
|
||||
public boolean isRejected() {
|
||||
return this == REJECTED;
|
||||
}
|
||||
|
||||
public boolean isTerminate() {
|
||||
return this == TERMINATE;
|
||||
}
|
||||
}
|
||||
|
||||
+9
-1
@@ -1611,6 +1611,14 @@ public class CommandWrapperBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder terminateAccount(final Long accountId) {
|
||||
this.actionName = "TERMINATE";
|
||||
this.entityName = "ACCOUNT";
|
||||
this.entityId = accountId;
|
||||
this.href = "/accounts/" + accountId + "/terminate";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder approveGSIMAccountApplication(final Long accountId) {
|
||||
this.actionName = "APPROVE";
|
||||
this.entityName = "GSIMACCOUNT";
|
||||
@@ -3565,7 +3573,7 @@ public class CommandWrapperBuilder {
|
||||
this.actionName = "ACTIVATE";
|
||||
this.entityName = "CURRENTACCOUNT";
|
||||
this.savingsId = accountId;
|
||||
this.entityId = null;
|
||||
this.entityId = accountId;
|
||||
this.href = "/currentaccounts/" + accountId + "?command=activate";
|
||||
return this;
|
||||
}
|
||||
|
||||
+2
-1
@@ -20,6 +20,7 @@ package org.apache.fineract.commands.service;
|
||||
|
||||
import static org.apache.fineract.commands.domain.CommandProcessingResultType.ERROR;
|
||||
import static org.apache.fineract.commands.domain.CommandProcessingResultType.PROCESSED;
|
||||
import static org.apache.fineract.commands.domain.CommandProcessingResultType.TERMINATE;
|
||||
import static org.apache.http.HttpStatus.SC_OK;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
@@ -181,7 +182,7 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
||||
currentSource.setResultStatusCode(SC_OK);
|
||||
currentSource.updateForAudit(result);
|
||||
currentSource.setResult(toApiResultJsonSerializer.serializeResult(result));
|
||||
currentSource.setStatus(PROCESSED);
|
||||
currentSource.setStatus("TERMINATE".equals(wrapper.getActionName()) ? TERMINATE : PROCESSED);
|
||||
|
||||
// Return saved command source
|
||||
return commandSourceService.saveResultSameTransaction(currentSource);
|
||||
|
||||
+1
-1
@@ -224,7 +224,7 @@ public final class DepositsApiConstants {
|
||||
* Deposit Product Parameters
|
||||
*/
|
||||
private static final Set<String> DEPOSIT_PRODUCT_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,
|
||||
monthDayFormatParamName, nameParamName, shortNameParamName, descriptionParamName, currencyCodeParamName,
|
||||
dateFormatParamName, monthDayFormatParamName, nameParamName, shortNameParamName, descriptionParamName, currencyCodeParamName,
|
||||
digitsAfterDecimalParamName, inMultiplesOfParamName, nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName,
|
||||
interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName,
|
||||
lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, accountingRuleParamName, chargesParamName,
|
||||
|
||||
+1
@@ -170,6 +170,7 @@ public final class SavingsAccountData implements Serializable {
|
||||
private Boolean balanceSwapOption;
|
||||
private Boolean msme;
|
||||
private Boolean autoDebitFacility;
|
||||
private Boolean terminateTrue;
|
||||
private String avgBalanceCalcPeriod;
|
||||
private String validForPeriod;
|
||||
private Boolean odAccountClosedZeroBalanceInDayClosing;
|
||||
|
||||
+3
@@ -159,6 +159,9 @@ public final class SavingsProductData implements Serializable {
|
||||
@Getter @Setter private Map<String, Object> depositLimitKyc;
|
||||
@Getter @Setter private Map<String, List<Map<String, Object>>> otherGlMappingsMap;
|
||||
|
||||
// True when a row exists in m_saving_product_temp with status = PENDING for this product - that row is the lock.
|
||||
@Getter @Setter private Boolean isPendingApproval;
|
||||
|
||||
public static SavingsProductData template(final CurrencyData currency, final EnumOptionData interestCompoundingPeriodType,
|
||||
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
|
||||
final EnumOptionData interestCalculationDaysInYearType, final EnumOptionData accountingRule,
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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.savings.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* Read-side view of a single pending Savings Product edit row (m_savings_product_temp), for the Approvals ->
|
||||
* Pending Savings Product Edits screen.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class SavingsProductTempData implements Serializable {
|
||||
|
||||
private Long id;
|
||||
private Long savingsProductId;
|
||||
private String savingsProductName;
|
||||
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 Map<String, Object> proposedChanges;
|
||||
|
||||
public SavingsProductTempData() {}
|
||||
}
|
||||
+7
-1
@@ -36,7 +36,8 @@ public enum SavingsAccountStatusType {
|
||||
REJECTED(500, "savingsAccountStatusType.rejected"), //
|
||||
CLOSED(600, "savingsAccountStatusType.closed"), //
|
||||
PRE_MATURE_CLOSURE(700, "savingsAccountStatusType.pre.mature.closure"), //
|
||||
MATURED(800, "savingsAccountStatusType.matured"); //
|
||||
MATURED(800, "savingsAccountStatusType.matured"), //
|
||||
TERMINATED(900, "savingsAccountStatusType.terminated"); //
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
@@ -53,6 +54,7 @@ public enum SavingsAccountStatusType {
|
||||
case 600 -> SavingsAccountStatusType.CLOSED;
|
||||
case 700 -> SavingsAccountStatusType.PRE_MATURE_CLOSURE;
|
||||
case 800 -> SavingsAccountStatusType.MATURED;
|
||||
case 900 -> SavingsAccountStatusType.TERMINATED;
|
||||
default -> SavingsAccountStatusType.INVALID;
|
||||
};
|
||||
}
|
||||
@@ -113,4 +115,8 @@ public enum SavingsAccountStatusType {
|
||||
public boolean isPreMatureClosure() {
|
||||
return this.value.equals(SavingsAccountStatusType.PRE_MATURE_CLOSURE.getValue());
|
||||
}
|
||||
|
||||
public boolean isTerminated() {
|
||||
return this.value.equals(SavingsAccountStatusType.TERMINATED.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -285,6 +285,11 @@ public final class SavingsEnumerations {
|
||||
SavingsAccountStatusType.MATURED.getCode(), "Matured", submittedAndPendingApproval, isApproved, isRejected,
|
||||
isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold, matured);
|
||||
break;
|
||||
case TERMINATED:
|
||||
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.TERMINATED.getValue().longValue(),
|
||||
SavingsAccountStatusType.TERMINATED.getCode(), "Terminated", submittedAndPendingApproval, isApproved, isRejected,
|
||||
isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold, matured);
|
||||
break;
|
||||
}
|
||||
return optionData;
|
||||
}
|
||||
|
||||
+52
-5
@@ -39,6 +39,7 @@ import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
|
||||
import org.apache.fineract.organisation.office.domain.Office;
|
||||
import org.apache.fineract.organisation.staff.domain.Staff;
|
||||
import org.apache.fineract.portfolio.client.domain.Client;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_current_account", uniqueConstraints = {
|
||||
@@ -102,6 +103,30 @@ public class CurrentAccount extends AbstractAuditableCustom {
|
||||
@Column(name = "closedon_date")
|
||||
private LocalDate closedOnDate;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "submittedon_userid")
|
||||
private AppUser submittedOnByUser;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "approvedon_userid")
|
||||
private AppUser approvedOnByUser;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "activatedon_userid")
|
||||
private AppUser activatedOnByUser;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "rejectedon_userid")
|
||||
private AppUser rejectedOnByUser;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "withdrawnon_userid")
|
||||
private AppUser withdrawnOnByUser;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "closedon_userid")
|
||||
private AppUser closedOnByUser;
|
||||
|
||||
// Currency (copied from product at account creation)
|
||||
@Column(name = "currency_code", nullable = false, length = 3)
|
||||
private String currencyCode;
|
||||
@@ -304,6 +329,12 @@ public class CurrentAccount extends AbstractAuditableCustom {
|
||||
@Column(name = "od_account_closed_zero_balance_in_day_closing")
|
||||
private boolean odAccountClosedZeroBalanceInDayClosing = false;
|
||||
|
||||
@Column(name = "is_linking_enable", nullable = false)
|
||||
private boolean shareLinkingEnabled = false;
|
||||
|
||||
@Column(name = "terminate_true", nullable = false, columnDefinition = "boolean default false")
|
||||
private boolean terminateTrue = false;
|
||||
|
||||
|
||||
// Relationships
|
||||
@OneToMany(mappedBy = "currentAccount", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||
@@ -373,29 +404,34 @@ public class CurrentAccount extends AbstractAuditableCustom {
|
||||
|
||||
public void updateAccountNo(String accountNo) { this.accountNo = accountNo; }
|
||||
|
||||
public void approve(LocalDate approvedOnDate) {
|
||||
public void approve(LocalDate approvedOnDate, AppUser approvedOnByUser) {
|
||||
this.statusEnum = 200;
|
||||
this.approvedOnDate = approvedOnDate;
|
||||
this.approvedOnByUser = approvedOnByUser;
|
||||
}
|
||||
|
||||
public void activate(LocalDate activatedOnDate) {
|
||||
public void activate(LocalDate activatedOnDate, AppUser activatedOnByUser) {
|
||||
this.statusEnum = 300;
|
||||
this.activatedOnDate = activatedOnDate;
|
||||
this.activatedOnByUser = activatedOnByUser;
|
||||
}
|
||||
|
||||
public void reject(LocalDate rejectedOnDate) {
|
||||
public void reject(LocalDate rejectedOnDate, AppUser rejectedOnByUser) {
|
||||
this.statusEnum = 500;
|
||||
this.rejectedOnDate = rejectedOnDate;
|
||||
this.rejectedOnByUser = rejectedOnByUser;
|
||||
}
|
||||
|
||||
public void withdraw(LocalDate withdrawnOnDate) {
|
||||
public void withdraw(LocalDate withdrawnOnDate, AppUser withdrawnOnByUser) {
|
||||
this.statusEnum = 400;
|
||||
this.withdrawnOnDate = withdrawnOnDate;
|
||||
this.withdrawnOnByUser = withdrawnOnByUser;
|
||||
}
|
||||
|
||||
public void close(LocalDate closedOnDate) {
|
||||
public void close(LocalDate closedOnDate, AppUser closedOnByUser) {
|
||||
this.statusEnum = 600;
|
||||
this.closedOnDate = closedOnDate;
|
||||
this.closedOnByUser = closedOnByUser;
|
||||
}
|
||||
|
||||
public void setMinRequiredOpeningBalance(java.math.BigDecimal val) { this.minRequiredOpeningBalance = val; }
|
||||
@@ -406,4 +442,15 @@ public class CurrentAccount extends AbstractAuditableCustom {
|
||||
public void setNominalAnnualInterestRateOverdraft(java.math.BigDecimal val) { this.nominalAnnualInterestRateOverdraft = val; }
|
||||
public void setWithdrawalFeeForTransfer(boolean val) { this.withdrawalFeeForTransfer = val; }
|
||||
public void setMinOverdraftForInterestCalculation(java.math.BigDecimal val) { this.minOverdraftForInterestCalculation = val; }
|
||||
|
||||
public boolean isShareLinkingEnabled() { return shareLinkingEnabled; }
|
||||
public void setShareLinkingEnabled(boolean shareLinkingEnabled) { this.shareLinkingEnabled = shareLinkingEnabled; }
|
||||
|
||||
public boolean isTerminateTrue() { return terminateTrue; }
|
||||
public void setTerminateTrue(boolean terminateTrue) { this.terminateTrue = terminateTrue; }
|
||||
|
||||
public void terminate() {
|
||||
this.terminateTrue = true;
|
||||
this.statusEnum = 900;
|
||||
}
|
||||
}
|
||||
|
||||
+31
-10
@@ -37,6 +37,31 @@ public class CurrentAccountNumberGenerator {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
private String getNextSequence() {
|
||||
final java.util.List<String> accountNos = this.jdbcTemplate.query(
|
||||
"SELECT account_no FROM m_current_account WHERE account_no IS NOT NULL",
|
||||
(rs, rowNum) -> rs.getString(1));
|
||||
|
||||
long maxVal = 0;
|
||||
for (String accNo : accountNos) {
|
||||
if (accNo != null && accNo.length() >= 5) {
|
||||
String suffix = accNo.substring(accNo.length() - 5);
|
||||
try {
|
||||
long val = Long.parseLong(suffix);
|
||||
if (val > maxVal) {
|
||||
maxVal = val;
|
||||
}
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
String nextSeq = StringUtils.leftPad(String.valueOf(maxVal + 1), 5, '0');
|
||||
if (nextSeq.length() > 5) {
|
||||
nextSeq = nextSeq.substring(nextSeq.length() - 5);
|
||||
}
|
||||
return nextSeq;
|
||||
}
|
||||
|
||||
public String generate(CurrentAccount account) {
|
||||
Long productId = account.getProductId();
|
||||
|
||||
@@ -59,11 +84,10 @@ public class CurrentAccountNumberGenerator {
|
||||
String officeId = StringUtils.leftPad(account.getOffice().getId().toString(), 2, '0');
|
||||
if (officeId.length() > 2) officeId = officeId.substring(officeId.length() - 2);
|
||||
|
||||
// Account ID — 5 digits
|
||||
String accountId = StringUtils.leftPad(account.getId().toString(), 5, '0');
|
||||
if (accountId.length() > 5) accountId = accountId.substring(accountId.length() - 5);
|
||||
// Next sequence from existing account numbers
|
||||
String nextSeq = getNextSequence();
|
||||
|
||||
return CBS_PREFIX + glCode + officeId + accountId;
|
||||
return CBS_PREFIX + glCode + officeId + nextSeq;
|
||||
}
|
||||
|
||||
public String preview(Long productId, Long officeId) {
|
||||
@@ -80,13 +104,10 @@ public class CurrentAccountNumberGenerator {
|
||||
String officeStr = StringUtils.leftPad(officeId != null ? officeId.toString() : "0", 2, '0');
|
||||
if (officeStr.length() > 2) officeStr = officeStr.substring(officeStr.length() - 2);
|
||||
|
||||
// Next account ID estimate from m_current_account
|
||||
Long maxId = jdbcTemplate.query(
|
||||
"SELECT MAX(id) FROM m_current_account",
|
||||
rs -> rs.next() ? rs.getLong(1) : 0L);
|
||||
String nextSeq = StringUtils.leftPad(String.valueOf((maxId != null ? maxId : 0L) + 1), 5, '0');
|
||||
if (nextSeq.length() > 5) nextSeq = nextSeq.substring(nextSeq.length() - 5);
|
||||
// Next sequence from existing account numbers
|
||||
String nextSeq = getNextSequence();
|
||||
|
||||
return CBS_PREFIX + glCode + officeStr + nextSeq;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
@@ -40,4 +40,6 @@ public interface CurrentAccountWritePlatformService {
|
||||
CommandProcessingResult modifyApplication(Long accountId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult deleteApplication(Long accountId);
|
||||
|
||||
CommandProcessingResult terminateAccount(Long accountId, JsonCommand command);
|
||||
}
|
||||
|
||||
+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.currentproduct.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
public class CurrentProductEditPendingApprovalException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public CurrentProductEditPendingApprovalException(final Long productId) {
|
||||
super("error.msg.currentproduct.edit.pending.approval",
|
||||
"Current product with identifier " + productId + " already has an edit pending approval", productId);
|
||||
}
|
||||
}
|
||||
+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.currentproduct.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
public class CurrentProductNoPendingEditException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public CurrentProductNoPendingEditException(final Long productId) {
|
||||
super("error.msg.currentproduct.no.pending.edit",
|
||||
"Current product with identifier " + productId + " has no edit pending approval", productId);
|
||||
}
|
||||
}
|
||||
+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.currentproduct.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
public class CurrentProductSelfApprovalNotAllowedException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public CurrentProductSelfApprovalNotAllowedException(final Long productId) {
|
||||
super("error.msg.currentproduct.self.approval.not.allowed",
|
||||
"The maker of the pending edit for current product " + productId + " cannot also approve or reject it", productId);
|
||||
}
|
||||
}
|
||||
+5
@@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.currentproduct.service;
|
||||
|
||||
import java.util.Collection;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||
|
||||
public interface CurrentProductReadPlatformService {
|
||||
|
||||
@@ -28,4 +29,8 @@ public interface CurrentProductReadPlatformService {
|
||||
Collection<SavingsProductData> retrieveAllForLookup();
|
||||
|
||||
SavingsProductData retrieveOne(Long productId);
|
||||
|
||||
Collection<SavingsProductTempData> retrieveAllPendingEdits();
|
||||
|
||||
SavingsProductTempData retrievePendingEdit(Long productId);
|
||||
}
|
||||
|
||||
+77
-1
@@ -42,13 +42,23 @@ import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilit
|
||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityConfigRepository;
|
||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimit;
|
||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimitRepository;
|
||||
import org.apache.fineract.portfolio.currentproduct.exception.CurrentProductNoPendingEditException;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEdit;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFacilityLimitRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFieldMapper;
|
||||
import java.util.Set;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class CurrentProductReadPlatformServiceImpl implements CurrentProductReadPlatformService {
|
||||
@@ -58,6 +68,11 @@ public class CurrentProductReadPlatformServiceImpl implements CurrentProductRead
|
||||
private final CurrentProductFacilityConfigRepository facilityConfigRepository;
|
||||
private final CurrentProductFacilityLimitRepository facilityLimitRepository;
|
||||
private final CurrentProductDepositLimitKycRepository depositLimitKycRepository;
|
||||
private final ProductPendingEditRepository productPendingEditRepository;
|
||||
private final ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository;
|
||||
private final ProductPendingEditChargeRepository pendingEditChargeRepository;
|
||||
private final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository;
|
||||
private final ProductPendingEditFacilityLimitRepository pendingEditFacilityLimitRepository;
|
||||
|
||||
private final CurrentProductMapper currentProductRowMapper = new CurrentProductMapper();
|
||||
private final CurrentProductLookupMapper currentProductLookupsRowMapper = new CurrentProductLookupMapper();
|
||||
@@ -147,6 +162,64 @@ public class CurrentProductReadPlatformServiceImpl implements CurrentProductRead
|
||||
}
|
||||
}
|
||||
|
||||
private static final String PENDING_EDIT_SCHEMA = "spt.id as tempId, spt.product_id as savingsProductId, "
|
||||
+ "cp.name as savingsProductName, spt.createdby_id as makerId, maker.username as makerUsername, "
|
||||
+ "spt.lastmodifiedby_id as checkerId, checker.username as checkerUsername, spt.created_date as createdOn, "
|
||||
+ "spt.lastmodified_date as checkedOn, spt.status as status, spt.reject_reason as rejectReason "
|
||||
+ "from m_saving_product_temp spt "
|
||||
+ "join m_current_product cp on cp.id = spt.product_id "
|
||||
+ "join m_appuser maker on maker.id = spt.createdby_id "
|
||||
+ "left join m_appuser checker on checker.id = spt.lastmodifiedby_id "
|
||||
+ "where spt.product_type = 'CURRENT' ";
|
||||
|
||||
private RowMapper<SavingsProductTempData> getPendingEditRowMapper() {
|
||||
return (rs, rowNum) -> {
|
||||
final SavingsProductTempData data = new SavingsProductTempData();
|
||||
data.setId(rs.getLong("tempId"));
|
||||
data.setSavingsProductId(rs.getLong("savingsProductId"));
|
||||
data.setSavingsProductName(rs.getString("savingsProductName"));
|
||||
data.setMakerId(rs.getLong("makerId"));
|
||||
data.setMakerUsername(rs.getString("makerUsername"));
|
||||
data.setCheckerId(JdbcSupport.getLong(rs, "checkerId"));
|
||||
data.setCheckerUsername(rs.getString("checkerUsername"));
|
||||
data.setCreatedOn(rs.getTimestamp("createdOn") == null ? null : rs.getTimestamp("createdOn").toLocalDateTime());
|
||||
data.setCheckedOn(rs.getTimestamp("checkedOn") == null ? null : rs.getTimestamp("checkedOn").toLocalDateTime());
|
||||
data.setStatus(rs.getString("status"));
|
||||
data.setRejectReason(rs.getString("rejectReason"));
|
||||
|
||||
final Long tempId = data.getId();
|
||||
final java.util.Optional<ProductPendingEdit> opt = this.productPendingEditRepository.findById(tempId);
|
||||
if (opt.isPresent()) {
|
||||
final ProductPendingEdit pendingEdit = opt.get();
|
||||
final Set<String> submittedFields = new java.util.HashSet<>(
|
||||
this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId()));
|
||||
final Map<String, Object> proposedChanges = ProductPendingEditFieldMapper.reconstructProposedChangesMap(
|
||||
pendingEdit, submittedFields, this.pendingEditChargeRepository, this.pendingEditGlMappingRepository,
|
||||
this.pendingEditFacilityLimitRepository, GSON);
|
||||
data.setProposedChanges(proposedChanges);
|
||||
}
|
||||
return data;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<SavingsProductTempData> retrieveAllPendingEdits() {
|
||||
this.context.authenticatedUser();
|
||||
final String sql = "select " + PENDING_EDIT_SCHEMA + "and spt.status = 'PENDING' order by spt.created_date desc";
|
||||
return this.jdbcTemplate.query(sql, getPendingEditRowMapper());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavingsProductTempData retrievePendingEdit(final Long productId) {
|
||||
this.context.authenticatedUser();
|
||||
final String sql = "select " + PENDING_EDIT_SCHEMA + "and spt.status = 'PENDING' and spt.product_id = ?";
|
||||
try {
|
||||
return this.jdbcTemplate.queryForObject(sql, getPendingEditRowMapper(), new Object[] { productId }); // NOSONAR
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new CurrentProductNoPendingEditException(productId);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CurrentProductMapper implements RowMapper<SavingsProductData> {
|
||||
|
||||
private final String schemaSql;
|
||||
@@ -197,7 +270,9 @@ public class CurrentProductReadPlatformServiceImpl implements CurrentProductRead
|
||||
sqlBuilder.append("cp.account_closed_zero_balance_in_day_closing as accountClosedZeroBalanceInDayClosing, ");
|
||||
sqlBuilder.append("cp.balance_swap_option as balanceSwapOption, cp.msme as msme, cp.auto_debit_facility as autoDebitFacility, ");
|
||||
sqlBuilder.append("cp.avg_balance_calc_period as avgBalanceCalcPeriod, cp.valid_for_period as validForPeriod, ");
|
||||
sqlBuilder.append("cp.od_account_closed_zero_balance_in_day_closing as odAccountClosedZeroBalanceInDayClosing ");
|
||||
sqlBuilder.append("cp.od_account_closed_zero_balance_in_day_closing as odAccountClosedZeroBalanceInDayClosing, ");
|
||||
sqlBuilder.append("exists(select 1 from m_saving_product_temp spt where spt.product_id = cp.id "
|
||||
+ "and spt.product_type = 'CURRENT' and spt.status = 'PENDING') as isPendingApproval ");
|
||||
|
||||
sqlBuilder.append("from m_current_product cp ");
|
||||
sqlBuilder.append("join m_currency curr on curr.code = cp.currency_code ");
|
||||
@@ -321,6 +396,7 @@ public class CurrentProductReadPlatformServiceImpl implements CurrentProductRead
|
||||
productData.setAvgBalanceCalcPeriod(rs.getString("avgBalanceCalcPeriod"));
|
||||
productData.setValidForPeriod(rs.getString("validForPeriod"));
|
||||
productData.setOdAccountClosedZeroBalanceInDayClosing(rs.getBoolean("odAccountClosedZeroBalanceInDayClosing"));
|
||||
productData.setIsPendingApproval(rs.getBoolean("isPendingApproval"));
|
||||
|
||||
return productData;
|
||||
}
|
||||
|
||||
+17
@@ -28,4 +28,21 @@ public interface CurrentProductWritePlatformService {
|
||||
CommandProcessingResult update(Long productId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult delete(Long productId);
|
||||
|
||||
/**
|
||||
* Submits an edit for approval instead of applying it directly. Fails if a pending edit already exists for this
|
||||
* product (that pending row is itself the lock).
|
||||
*/
|
||||
CommandProcessingResult submitEditForApproval(Long productId, JsonCommand command);
|
||||
|
||||
/**
|
||||
* Replays the stored pending edit through the normal update path and marks it approved. Fails if the approving
|
||||
* user is the same user who submitted the edit.
|
||||
*/
|
||||
CommandProcessingResult approveEdit(Long productId);
|
||||
|
||||
/**
|
||||
* Discards the pending edit. Fails if the rejecting user is the same user who submitted the edit.
|
||||
*/
|
||||
CommandProcessingResult rejectEdit(Long productId, String rejectReason);
|
||||
}
|
||||
|
||||
+245
@@ -31,6 +31,9 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
|
||||
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;
|
||||
@@ -38,6 +41,8 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuild
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||
import org.apache.fineract.portfolio.currentproduct.data.CurrentProductDataValidator;
|
||||
@@ -50,10 +55,26 @@ import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilit
|
||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimit;
|
||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimitRepository;
|
||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductRepository;
|
||||
import org.apache.fineract.portfolio.currentproduct.exception.CurrentProductEditPendingApprovalException;
|
||||
import org.apache.fineract.portfolio.currentproduct.exception.CurrentProductNoPendingEditException;
|
||||
import org.apache.fineract.portfolio.currentproduct.exception.CurrentProductSelfApprovalNotAllowedException;
|
||||
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEdit;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedField;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditCharge;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFacilityLimit;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFacilityLimitRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMapping;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFieldMapper;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -61,6 +82,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@RequiredArgsConstructor
|
||||
public class CurrentProductWritePlatformServiceJpaRepositoryImpl implements CurrentProductWritePlatformService {
|
||||
|
||||
private static final com.google.gson.Gson GSON = new com.google.gson.Gson();
|
||||
private final PlatformSecurityContext context;
|
||||
private final CurrentProductRepository currentProductRepository;
|
||||
private final CurrentProductDataValidator fromApiJsonDataValidator;
|
||||
@@ -69,6 +91,13 @@ public class CurrentProductWritePlatformServiceJpaRepositoryImpl implements Curr
|
||||
private final CurrentProductFacilityConfigRepository facilityConfigRepository;
|
||||
private final CurrentProductFacilityLimitRepository facilityLimitRepository;
|
||||
private final CurrentProductDepositLimitKycRepository depositLimitKycRepository;
|
||||
private final ProductPendingEditRepository productPendingEditRepository;
|
||||
private final ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository;
|
||||
private final ProductPendingEditChargeRepository pendingEditChargeRepository;
|
||||
private final ProductPendingEditFacilityLimitRepository pendingEditFacilityLimitRepository;
|
||||
private final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final CommandSourceRepository commandSourceRepository;
|
||||
|
||||
private void handleDataIntegrityIssues(final JsonCommand command, final Throwable realCause, final Exception dae) {
|
||||
String msgCode = "error.msg." + DepositsApiConstants.CURRENT_PRODUCT_RESOURCE_NAME;
|
||||
@@ -251,6 +280,222 @@ public class CurrentProductWritePlatformServiceJpaRepositoryImpl implements Curr
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult submitEditForApproval(final Long productId, final JsonCommand command) {
|
||||
|
||||
final AppUser maker = this.context.authenticatedUser();
|
||||
final CurrentProduct product = this.currentProductRepository.findById(productId)
|
||||
.orElseThrow(() -> new SavingsProductNotFoundException(productId));
|
||||
|
||||
if (this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_CURRENT, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.isPresent()) {
|
||||
throw new CurrentProductEditPendingApprovalException(productId);
|
||||
}
|
||||
|
||||
this.fromApiJsonDataValidator.validateForUpdate(command.json(), product);
|
||||
|
||||
final ProductPendingEdit pendingEdit = new ProductPendingEdit(ProductPendingEdit.TYPE_CURRENT, productId, maker);
|
||||
|
||||
final java.util.Set<String> submitted = ProductPendingEditFieldMapper.populateCoreFields(command, pendingEdit.getCoreFields());
|
||||
final java.util.Set<String> currentOnlySubmitted = ProductPendingEditFieldMapper.populateCurrentOnlyFields(command, pendingEdit.getCurrentOnlyFields());
|
||||
final boolean facilityConfigSubmitted = ProductPendingEditFieldMapper.populateFacilityConfig(command, pendingEdit.getFacilityConfigFields());
|
||||
final boolean depositLimitKycSubmitted = ProductPendingEditFieldMapper.populateDepositLimitKyc(command, pendingEdit.getDepositLimitKycFields());
|
||||
final boolean glMappingSubmitted = ProductPendingEditFieldMapper.populateGlAccountMapping(command, pendingEdit.getGlAccountMappingFields());
|
||||
|
||||
if (!currentOnlySubmitted.isEmpty()) {
|
||||
submitted.add("avgBalanceCalcPeriod");
|
||||
submitted.add("validForPeriod");
|
||||
submitted.add("odAccountClosedZeroBalanceInDayClosing");
|
||||
}
|
||||
if (facilityConfigSubmitted) {
|
||||
submitted.add("accountFacilityConfig");
|
||||
}
|
||||
if (depositLimitKycSubmitted) {
|
||||
submitted.add("depositLimitKyc");
|
||||
}
|
||||
if (glMappingSubmitted) {
|
||||
submitted.add("glAccountMapping");
|
||||
}
|
||||
|
||||
final CommandSource auditEntry = CommandSource.builder() //
|
||||
.actionName("UPDATE") //
|
||||
.entityName("CURRENTPRODUCT") //
|
||||
.resourceGetUrl("/currentproducts/" + productId) //
|
||||
.resourceId(productId) //
|
||||
.commandAsJson(command.json()) //
|
||||
.maker(maker) //
|
||||
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||
.idempotencyKey(java.util.UUID.randomUUID().toString()) //
|
||||
.sanitized(false) //
|
||||
.build();
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
for (final String fieldName : submitted) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, fieldName));
|
||||
}
|
||||
|
||||
final List<Long> chargeIds = ProductPendingEditFieldMapper.extractChargeIds(command);
|
||||
if (chargeIds != null) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "charges"));
|
||||
for (final Long chargeId : chargeIds) {
|
||||
this.pendingEditChargeRepository.save(new ProductPendingEditCharge(pendingEdit, chargeId));
|
||||
}
|
||||
}
|
||||
|
||||
final List<ProductPendingEditFacilityLimit> facilityLimits = ProductPendingEditFieldMapper.extractFacilityLimits(command, pendingEdit);
|
||||
if (facilityLimits != null) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "accountFacilityLimits"));
|
||||
this.pendingEditFacilityLimitRepository.saveAll(facilityLimits);
|
||||
}
|
||||
|
||||
final List<ProductPendingEditGlMapping> glMappings = ProductPendingEditFieldMapper.extractGlArrayMappings(command, pendingEdit);
|
||||
if (glMappings != null) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "glArrayMappings"));
|
||||
this.pendingEditGlMappingRepository.saveAll(glMappings);
|
||||
}
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(product.getId()) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult approveEdit(final Long productId) {
|
||||
|
||||
final AppUser checker = this.context.authenticatedUser();
|
||||
final ProductPendingEdit pendingEdit = this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_CURRENT, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.orElseThrow(() -> new CurrentProductNoPendingEditException(productId));
|
||||
|
||||
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||
throw new CurrentProductSelfApprovalNotAllowedException(productId);
|
||||
}
|
||||
|
||||
final Set<String> submittedFields = new java.util.HashSet<>(
|
||||
this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId()));
|
||||
final JsonObject root = new JsonObject();
|
||||
root.addProperty("locale", "en");
|
||||
root.addProperty("dateFormat", "yyyy-MM-dd");
|
||||
ProductPendingEditFieldMapper.writeCoreFieldsToJson(root, pendingEdit.getCoreFields(), submittedFields);
|
||||
ProductPendingEditFieldMapper.writeCurrentOnlyFieldsToJson(root, pendingEdit.getCurrentOnlyFields(), submittedFields);
|
||||
ProductPendingEditFieldMapper.writeFacilityConfigToJson(root, pendingEdit.getFacilityConfigFields(),
|
||||
submittedFields.contains("accountFacilityConfig"));
|
||||
ProductPendingEditFieldMapper.writeDepositLimitKycToJson(root, pendingEdit.getDepositLimitKycFields(),
|
||||
submittedFields.contains("depositLimitKyc"));
|
||||
if (submittedFields.contains("glAccountMapping")) {
|
||||
ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields());
|
||||
}
|
||||
if (submittedFields.contains("charges")) {
|
||||
ProductPendingEditFieldMapper.writeChargesToJson(root,
|
||||
this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
if (submittedFields.contains("accountFacilityLimits")) {
|
||||
ProductPendingEditFieldMapper.writeFacilityLimitsToJson(root,
|
||||
this.pendingEditFacilityLimitRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
if (submittedFields.contains("glArrayMappings")) {
|
||||
ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root,
|
||||
this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
final JsonCommand replayedCommand = JsonCommand.fromJsonElement(productId, root, this.fromApiJsonHelper);
|
||||
final CommandProcessingResult result = update(productId, replayedCommand);
|
||||
|
||||
pendingEdit.markApproved(checker);
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
final CommandSource auditEntry = findOrCreateAuditEntry(pendingEdit, productId);
|
||||
auditEntry.markAsChecked(checker);
|
||||
auditEntry.updateForAudit(result);
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private CommandSource findOrCreateAuditEntry(final ProductPendingEdit pendingEdit, final Long productId) {
|
||||
if (pendingEdit.getCommandSourceId() != null) {
|
||||
final java.util.Optional<CommandSource> existing = this.commandSourceRepository.findById(pendingEdit.getCommandSourceId());
|
||||
if (existing.isPresent()) {
|
||||
return existing.get();
|
||||
}
|
||||
}
|
||||
final Set<String> submittedFields = new java.util.HashSet<>(
|
||||
this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId()));
|
||||
final JsonObject root = new JsonObject();
|
||||
root.addProperty("locale", "en");
|
||||
root.addProperty("dateFormat", "yyyy-MM-dd");
|
||||
ProductPendingEditFieldMapper.writeCoreFieldsToJson(root, pendingEdit.getCoreFields(), submittedFields);
|
||||
ProductPendingEditFieldMapper.writeCurrentOnlyFieldsToJson(root, pendingEdit.getCurrentOnlyFields(), submittedFields);
|
||||
ProductPendingEditFieldMapper.writeFacilityConfigToJson(root, pendingEdit.getFacilityConfigFields(),
|
||||
submittedFields.contains("accountFacilityConfig"));
|
||||
ProductPendingEditFieldMapper.writeDepositLimitKycToJson(root, pendingEdit.getDepositLimitKycFields(),
|
||||
submittedFields.contains("depositLimitKyc"));
|
||||
if (submittedFields.contains("glAccountMapping")) {
|
||||
ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields());
|
||||
}
|
||||
if (submittedFields.contains("charges")) {
|
||||
ProductPendingEditFieldMapper.writeChargesToJson(root,
|
||||
this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
if (submittedFields.contains("accountFacilityLimits")) {
|
||||
ProductPendingEditFieldMapper.writeFacilityLimitsToJson(root,
|
||||
this.pendingEditFacilityLimitRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
if (submittedFields.contains("glArrayMappings")) {
|
||||
ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root,
|
||||
this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
final String commandJson = GSON.toJson(root);
|
||||
|
||||
final CommandSource auditEntry = CommandSource.builder() //
|
||||
.actionName("UPDATE") //
|
||||
.entityName("CURRENTPRODUCT") //
|
||||
.resourceGetUrl("/currentproducts/" + productId) //
|
||||
.resourceId(productId) //
|
||||
.commandAsJson(commandJson) //
|
||||
.maker(pendingEdit.getMaker()) //
|
||||
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||
.idempotencyKey(java.util.UUID.randomUUID().toString()) //
|
||||
.sanitized(false) //
|
||||
.build();
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
return auditEntry;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult rejectEdit(final Long productId, final String rejectReason) {
|
||||
|
||||
final AppUser checker = this.context.authenticatedUser();
|
||||
final ProductPendingEdit pendingEdit = this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_CURRENT, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.orElseThrow(() -> new CurrentProductNoPendingEditException(productId));
|
||||
|
||||
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||
throw new CurrentProductSelfApprovalNotAllowedException(productId);
|
||||
}
|
||||
|
||||
pendingEdit.markRejected(checker, rejectReason);
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
final CommandSource auditEntry = findOrCreateAuditEntry(pendingEdit, productId);
|
||||
auditEntry.markAsRejected(checker);
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(productId) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult delete(final Long productId) {
|
||||
|
||||
+28
-4
@@ -19,7 +19,14 @@
|
||||
package org.apache.fineract.portfolio.currentproduct.starter;
|
||||
|
||||
import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
|
||||
import org.apache.fineract.commands.domain.CommandSourceRepository;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFacilityLimitRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository;
|
||||
import org.apache.fineract.portfolio.currentproduct.data.CurrentProductDataValidator;
|
||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductAssembler;
|
||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductDepositLimitKycRepository;
|
||||
@@ -43,9 +50,17 @@ public class CurrentProductConfiguration {
|
||||
public CurrentProductReadPlatformService currentProductReadPlatformService(PlatformSecurityContext context,
|
||||
JdbcTemplate jdbcTemplate, CurrentProductFacilityConfigRepository currentFacilityConfigRepository,
|
||||
CurrentProductFacilityLimitRepository currentFacilityLimitRepository,
|
||||
CurrentProductDepositLimitKycRepository currentDepositLimitKycRepository) {
|
||||
CurrentProductDepositLimitKycRepository currentDepositLimitKycRepository,
|
||||
ProductPendingEditRepository productPendingEditRepository,
|
||||
ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository,
|
||||
ProductPendingEditChargeRepository pendingEditChargeRepository,
|
||||
ProductPendingEditGlMappingRepository pendingEditGlMappingRepository,
|
||||
ProductPendingEditFacilityLimitRepository pendingEditFacilityLimitRepository) {
|
||||
return new CurrentProductReadPlatformServiceImpl(context, jdbcTemplate, currentFacilityConfigRepository,
|
||||
currentFacilityLimitRepository, currentDepositLimitKycRepository);
|
||||
currentFacilityLimitRepository, currentDepositLimitKycRepository,
|
||||
productPendingEditRepository, pendingEditSubmittedFieldRepository,
|
||||
pendingEditChargeRepository, pendingEditGlMappingRepository,
|
||||
pendingEditFacilityLimitRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -56,9 +71,18 @@ public class CurrentProductConfiguration {
|
||||
ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService,
|
||||
CurrentProductFacilityConfigRepository currentFacilityConfigRepository,
|
||||
CurrentProductFacilityLimitRepository currentFacilityLimitRepository,
|
||||
CurrentProductDepositLimitKycRepository currentDepositLimitKycRepository) {
|
||||
CurrentProductDepositLimitKycRepository currentDepositLimitKycRepository,
|
||||
ProductPendingEditRepository productPendingEditRepository,
|
||||
ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository,
|
||||
ProductPendingEditChargeRepository pendingEditChargeRepository,
|
||||
ProductPendingEditFacilityLimitRepository pendingEditFacilityLimitRepository,
|
||||
ProductPendingEditGlMappingRepository pendingEditGlMappingRepository,
|
||||
FromJsonHelper fromApiJsonHelper,
|
||||
CommandSourceRepository commandSourceRepository) {
|
||||
return new CurrentProductWritePlatformServiceJpaRepositoryImpl(context, currentProductRepository, currentProductDataValidator,
|
||||
currentProductAssembler, accountMappingWritePlatformService, currentFacilityConfigRepository,
|
||||
currentFacilityLimitRepository, currentDepositLimitKycRepository);
|
||||
currentFacilityLimitRepository, currentDepositLimitKycRepository, productPendingEditRepository,
|
||||
pendingEditSubmittedFieldRepository, pendingEditChargeRepository, pendingEditFacilityLimitRepository,
|
||||
pendingEditGlMappingRepository, fromApiJsonHelper, commandSourceRepository);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -120,7 +120,7 @@ public class TellerManagementReadPlatformServiceImpl implements TellerManagement
|
||||
|
||||
try {
|
||||
final TellerMapper tm = new TellerMapper();
|
||||
final String sql = "select " + tm.schema() + " where t.id = ?";
|
||||
final String sql = "select " + tm.schema() + " where t.id = ? and t.is_deleted = false";
|
||||
|
||||
return this.jdbcTemplate.queryForObject(sql, tm, new Object[] { tellerId }); // NOSONAR
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
@@ -198,7 +198,7 @@ public class TellerManagementReadPlatformServiceImpl implements TellerManagement
|
||||
hierarchySearchString = hierarchy + "%";
|
||||
}
|
||||
final TellerMapper tm = new TellerMapper();
|
||||
final String sql = "select " + tm.schema() + "where o.hierarchy like ? order by o.hierarchy";
|
||||
final String sql = "select " + tm.schema() + "where o.hierarchy like ? and t.is_deleted = false order by o.hierarchy";
|
||||
|
||||
return this.jdbcTemplate.query(sql, tm, new Object[] { hierarchySearchString }); // NOSONAR
|
||||
}
|
||||
|
||||
+7
-12
@@ -46,6 +46,7 @@ import org.apache.fineract.organisation.teller.domain.Teller;
|
||||
import org.apache.fineract.organisation.teller.domain.TellerRepositoryWrapper;
|
||||
import org.apache.fineract.organisation.teller.exception.CashierExistForTellerException;
|
||||
import org.apache.fineract.organisation.teller.exception.CashierNotFoundException;
|
||||
import org.apache.fineract.organisation.teller.exception.TellerAlreadyExistsException;
|
||||
import org.apache.fineract.organisation.teller.serialization.TellerCommandFromApiJsonDeserializer;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
@@ -71,6 +72,10 @@ public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformSer
|
||||
try {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
if (this.tellerRepositoryWrapper.existsAny()) {
|
||||
throw new TellerAlreadyExistsException();
|
||||
}
|
||||
|
||||
final Long officeId = command.longValueOfParameterNamed("officeId");
|
||||
|
||||
this.fromApiJsonDeserializer.validateForCreateAndUpdateTeller(command.json());
|
||||
@@ -152,22 +157,12 @@ public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformSer
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult deleteTeller(Long tellerId) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
Teller teller = tellerRepositoryWrapper.findOneWithNotFoundDetection(tellerId);
|
||||
Set<Cashier> isTellerIdPresentInCashier = teller.getCashiers();
|
||||
|
||||
for (final Cashier tellerIdInCashier : isTellerIdPresentInCashier) {
|
||||
if (tellerIdInCashier.getTeller().getId().toString().equalsIgnoreCase(tellerId.toString())) {
|
||||
throw new CashierExistForTellerException(tellerId);
|
||||
}
|
||||
|
||||
}
|
||||
tellerRepositoryWrapper.delete(teller);
|
||||
teller.delete();
|
||||
tellerRepositoryWrapper.saveAndFlush(teller);
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(teller.getId()) //
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* 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.account.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
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.MediaType;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import com.google.gson.JsonElement;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/accounts/{accountId}/terminate")
|
||||
@Component
|
||||
@Tag(name = "Account Terminate", description = "Unified terminate endpoint for Savings, Current, FD and RD accounts.")
|
||||
@RequiredArgsConstructor
|
||||
public class AccountTerminateApiResource {
|
||||
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final DefaultToApiJsonSerializer<CommandProcessingResult> toApiJsonSerializer;
|
||||
private final FromJsonHelper fromJsonHelper;
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Terminate an account", description = "Terminates a Savings, Current, Fixed Deposit or Recurring Deposit account. Pass accountType in the request body: SAVINGS | CURRENT | FD | RD.")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String terminate(@PathParam("accountId") final Long accountId,
|
||||
final String apiRequestBodyAsJson) {
|
||||
|
||||
final String jsonBody = StringUtils.isBlank(apiRequestBodyAsJson) ? "{}" : apiRequestBodyAsJson;
|
||||
|
||||
final JsonElement root = this.fromJsonHelper.parse(jsonBody);
|
||||
final String accountType = this.fromJsonHelper.extractStringNamed("accountType", root);
|
||||
String entityName = "SAVINGSACCOUNT";
|
||||
if ("CURRENT".equalsIgnoreCase(accountType)) {
|
||||
entityName = "CURRENTACCOUNT";
|
||||
} else if ("FD".equalsIgnoreCase(accountType)) {
|
||||
entityName = "FIXEDDEPOSITACCOUNT";
|
||||
} else if ("RD".equalsIgnoreCase(accountType)) {
|
||||
entityName = "RECURRINGDEPOSITACCOUNT";
|
||||
}
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder()
|
||||
.withJson(jsonBody)
|
||||
.terminateAccount(accountId)
|
||||
.withEntityName(entityName)
|
||||
.build();
|
||||
final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.account.handler;
|
||||
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.account.service.AccountTerminateWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "ACCOUNT", action = "TERMINATE")
|
||||
public class AccountTerminateCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final AccountTerminateWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public AccountTerminateCommandHandler(final AccountTerminateWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.terminate(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* 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.account.service;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
|
||||
public interface AccountTerminateWritePlatformService {
|
||||
|
||||
CommandProcessingResult terminate(Long accountId, JsonCommand command);
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* 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.account.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Routes terminate to the correct account service based on the
|
||||
* "accountType" field in the JSON body:
|
||||
* SAVINGS → m_savings_account (depositType = savings)
|
||||
* FD → m_savings_account (depositType = 200)
|
||||
* RD → m_savings_account (depositType = 300)
|
||||
* CURRENT → m_current_account
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AccountTerminateWritePlatformServiceImpl implements AccountTerminateWritePlatformService {
|
||||
|
||||
private static final String PARAM_ACCOUNT_TYPE = "accountType";
|
||||
|
||||
private final SavingsAccountWritePlatformService savingsAccountWritePlatformService;
|
||||
private final CurrentAccountWritePlatformService currentAccountWritePlatformService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult terminate(final Long accountId, final JsonCommand command) {
|
||||
final String accountType = command.stringValueOfParameterNamed(PARAM_ACCOUNT_TYPE);
|
||||
|
||||
if (accountType == null || accountType.isBlank()) {
|
||||
throw new PlatformDataIntegrityException(
|
||||
"error.msg.account.terminate.accountType.required",
|
||||
"accountType is required: SAVINGS, CURRENT, FD or RD");
|
||||
}
|
||||
|
||||
return switch (accountType.toUpperCase()) {
|
||||
case "CURRENT" -> currentAccountWritePlatformService.terminateAccount(accountId, command);
|
||||
case "SAVINGS", "FD", "RD" -> savingsAccountWritePlatformService.terminate(accountId, command);
|
||||
default -> throw new PlatformDataIntegrityException(
|
||||
"error.msg.account.terminate.accountType.invalid",
|
||||
"Unknown accountType '" + accountType + "'. Expected SAVINGS, CURRENT, FD or RD.");
|
||||
};
|
||||
}
|
||||
}
|
||||
+112
@@ -37,20 +37,26 @@ import jakarta.ws.rs.QueryParam;
|
||||
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.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
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.exception.ResourceNotFoundException;
|
||||
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.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.security.exception.NoAuthorizationException;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.products.constants.ProductsApiConstants;
|
||||
import org.apache.fineract.portfolio.products.data.ProductData;
|
||||
import org.apache.fineract.portfolio.products.service.ShareProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||
import org.apache.fineract.portfolio.shareproducts.service.ShareProductWritePlatformService;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -61,11 +67,26 @@ import org.springframework.stereotype.Component;
|
||||
@RequiredArgsConstructor
|
||||
public class ProductsApiResource {
|
||||
|
||||
private static final String SHARE_PRODUCT_TYPE = "share";
|
||||
/**
|
||||
* These product types have their own dedicated endpoints (/savingsproducts/{id}, /currentproducts/{id},
|
||||
* /fixeddepositproducts/{id}, /recurringdepositproducts/{id}) which correctly route edits through
|
||||
* submitEditForApproval - the pending-edit row is the lock. This generic /v1/products/{type}/{productId} PUT
|
||||
* falls through to the native command-processing pipeline for any type not special-cased above, which would
|
||||
* bypass that lock entirely for these types. The Angular frontend never calls this generic path for them (only
|
||||
* for "share"), so blocking them here closes the bypass without removing any real functionality.
|
||||
*/
|
||||
private static final java.util.Set<String> PRODUCT_TYPES_WITH_DEDICATED_LOCKED_ENDPOINT = java.util.Set.of("savings", "current",
|
||||
"fixeddeposit", "recurringdeposit");
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final DefaultToApiJsonSerializer<ProductData> toApiJsonSerializer;
|
||||
private final PlatformSecurityContext platformSecurityContext;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final ShareProductWritePlatformService shareProductWritePlatformService;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final DefaultToApiJsonSerializer<SavingsProductTempData> tempDataApiJsonSerializer;
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@@ -171,9 +192,100 @@ public class ProductsApiResource {
|
||||
@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
this.platformSecurityContext.authenticatedUser();
|
||||
|
||||
if (SHARE_PRODUCT_TYPE.equalsIgnoreCase(productType)) {
|
||||
// Bypasses the native command-source/maker-checker pipeline by design, same as Savings/Current
|
||||
// Products: edits are held in m_savings_product_temp (a pending row there is itself the lock) and
|
||||
// only applied to m_share_product on approval - see the pending/approve/reject endpoints below.
|
||||
final JsonCommand command = JsonCommand.fromJsonElement(productId, this.fromApiJsonHelper.parse(apiRequestBodyAsJson),
|
||||
this.fromApiJsonHelper);
|
||||
final CommandProcessingResult result = this.shareProductWritePlatformService.submitEditForApproval(productId, command);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
if (PRODUCT_TYPES_WITH_DEDICATED_LOCKED_ENDPOINT.contains(productType.toLowerCase(java.util.Locale.ROOT))) {
|
||||
throw new ResourceNotFoundException("error.msg.product.generic.update.not.supported",
|
||||
"Updating a " + productType + " product through this generic endpoint is not supported - use its dedicated endpoint",
|
||||
new Object[] { productType });
|
||||
}
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateProduct(productType, productId)
|
||||
.withJson(apiRequestBodyAsJson).build();
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("pending")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List Share Products pending edit approval")
|
||||
public String retrieveAllPending(@PathParam("type") @Parameter(description = "type") final String productType,
|
||||
@Context final UriInfo uriInfo) {
|
||||
this.platformSecurityContext.authenticatedUser();
|
||||
try {
|
||||
String serviceName = productType + ProductsApiConstants.READPLATFORM_NAME;
|
||||
ShareProductReadPlatformService service = (ShareProductReadPlatformService) this.applicationContext.getBean(serviceName);
|
||||
final Collection<SavingsProductTempData> pendingEdits = service.retrieveAllPendingEdits();
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.tempDataApiJsonSerializer.serialize(settings, pendingEdits);
|
||||
} catch (BeansException e) {
|
||||
throw new ResourceNotFoundException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{productId}/pending")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve the pending edit for a Share Product")
|
||||
public String retrievePending(@PathParam("type") @Parameter(description = "type") final String productType,
|
||||
@PathParam("productId") @Parameter(description = "productId") final Long productId, @Context final UriInfo uriInfo) {
|
||||
this.platformSecurityContext.authenticatedUser();
|
||||
try {
|
||||
String serviceName = productType + ProductsApiConstants.READPLATFORM_NAME;
|
||||
ShareProductReadPlatformService service = (ShareProductReadPlatformService) this.applicationContext.getBean(serviceName);
|
||||
final SavingsProductTempData pendingEdit = service.retrievePendingEdit(productId);
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.tempDataApiJsonSerializer.serialize(settings, pendingEdit);
|
||||
} catch (BeansException e) {
|
||||
throw new ResourceNotFoundException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{productId}/approve")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Approve a pending Share Product edit")
|
||||
public String approvePending(@PathParam("type") @Parameter(description = "type") final String productType,
|
||||
@PathParam("productId") @Parameter(description = "productId") final Long productId) {
|
||||
if (this.platformSecurityContext.authenticatedUser().hasNotPermissionForAnyOf("APPROVE_SHAREPRODUCT")) {
|
||||
throw new NoAuthorizationException("User has no authority to approve share products");
|
||||
}
|
||||
final CommandProcessingResult result = this.shareProductWritePlatformService.approveEdit(productId);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{productId}/reject")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Reject a pending Share Product edit")
|
||||
public String rejectPending(@PathParam("type") @Parameter(description = "type") final String productType,
|
||||
@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
if (this.platformSecurityContext.authenticatedUser().hasNotPermissionForAnyOf("REJECT_SHAREPRODUCT")) {
|
||||
throw new NoAuthorizationException("User has no authority to reject share products");
|
||||
}
|
||||
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.shareProductWritePlatformService.rejectEdit(productId, rejectReason);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -22,6 +22,7 @@ import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.portfolio.products.data.ProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||
|
||||
public interface ShareProductReadPlatformService {
|
||||
|
||||
@@ -34,4 +35,8 @@ public interface ShareProductReadPlatformService {
|
||||
Set<String> getResponseDataParams();
|
||||
|
||||
Collection<ProductData> retrieveAllForLookup();
|
||||
|
||||
Collection<SavingsProductTempData> retrieveAllPendingEdits();
|
||||
|
||||
SavingsProductTempData retrievePendingEdit(Long productId);
|
||||
}
|
||||
|
||||
+28
-21
@@ -53,6 +53,9 @@ import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountReadPlatformService;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.CurrentAccountTemplateReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -72,6 +75,7 @@ public class CurrentAccountsApiResource {
|
||||
private final SqlValidator sqlValidator;
|
||||
private final CurrentAccountWritePlatformService currentAccountWritePlatformService;
|
||||
private final CurrentAccountReadPlatformService currentAccountReadPlatformService;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@@ -125,10 +129,11 @@ public class CurrentAccountsApiResource {
|
||||
@Operation(summary = "Submit new current account application")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String submitApplication(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final org.apache.fineract.infrastructure.core.api.JsonCommand command =
|
||||
org.apache.fineract.infrastructure.core.api.JsonCommand.fromJsonElement(null,
|
||||
JsonParser.parseString(apiRequestBodyAsJson != null ? apiRequestBodyAsJson : "{}"));
|
||||
final CommandProcessingResult result = currentAccountWritePlatformService.submitApplication(command);
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder() //
|
||||
.createCurrentAccount() //
|
||||
.withJson(apiRequestBodyAsJson) //
|
||||
.build();
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@@ -157,11 +162,11 @@ public class CurrentAccountsApiResource {
|
||||
public String update(@PathParam("accountId") final Long accountId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson,
|
||||
@QueryParam("command") final String commandParam) {
|
||||
|
||||
final org.apache.fineract.infrastructure.core.api.JsonCommand command =
|
||||
org.apache.fineract.infrastructure.core.api.JsonCommand.fromJsonElement(null,
|
||||
JsonParser.parseString(apiRequestBodyAsJson != null ? apiRequestBodyAsJson : "{}"));
|
||||
final CommandProcessingResult result = currentAccountWritePlatformService.modifyApplication(accountId, command);
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder() //
|
||||
.updateCurrentAccount(accountId) //
|
||||
.withJson(apiRequestBodyAsJson) //
|
||||
.build();
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@@ -180,30 +185,29 @@ public class CurrentAccountsApiResource {
|
||||
jsonApiRequest = "{}";
|
||||
}
|
||||
|
||||
final org.apache.fineract.infrastructure.core.api.JsonCommand command =
|
||||
org.apache.fineract.infrastructure.core.api.JsonCommand.fromJsonElement(null,
|
||||
JsonParser.parseString(jsonApiRequest));
|
||||
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(jsonApiRequest);
|
||||
|
||||
CommandProcessingResult result = null;
|
||||
CommandWrapper commandRequest = null;
|
||||
if (is(commandParam, "reject")) {
|
||||
result = currentAccountWritePlatformService.rejectApplication(accountId, command);
|
||||
commandRequest = builder.rejectCurrentAccountApplication(accountId).build();
|
||||
} else if (is(commandParam, "withdrawnByApplicant")) {
|
||||
result = currentAccountWritePlatformService.withdrawApplication(accountId, command);
|
||||
commandRequest = builder.withdrawCurrentAccountApplication(accountId).build();
|
||||
} else if (is(commandParam, "approve")) {
|
||||
result = currentAccountWritePlatformService.approveApplication(accountId, command);
|
||||
commandRequest = builder.approveCurrentAccountApplication(accountId).build();
|
||||
} else if (is(commandParam, "undoapproval")) {
|
||||
result = currentAccountWritePlatformService.undoApproval(accountId, command);
|
||||
commandRequest = builder.undoCurrentAccountApplication(accountId).build();
|
||||
} else if (is(commandParam, "activate")) {
|
||||
result = currentAccountWritePlatformService.activate(accountId, command);
|
||||
commandRequest = builder.currentAccountActivation(accountId).build();
|
||||
} else if (is(commandParam, "close")) {
|
||||
result = currentAccountWritePlatformService.closeAccount(accountId, command);
|
||||
commandRequest = builder.closeCurrentAccountApplication(accountId).build();
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
if (commandRequest == null) {
|
||||
throw new UnrecognizedQueryParamException("command", commandParam,
|
||||
new Object[] { "reject", "withdrawnByApplicant", "approve", "undoapproval", "activate", "close" });
|
||||
}
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@@ -214,7 +218,10 @@ public class CurrentAccountsApiResource {
|
||||
@Operation(summary = "Delete a current account application")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String delete(@PathParam("accountId") final Long accountId) {
|
||||
final CommandProcessingResult result = currentAccountWritePlatformService.deleteApplication(accountId);
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder() //
|
||||
.deleteCurrentAccount(accountId) //
|
||||
.build();
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
|
||||
+74
-3
@@ -53,10 +53,13 @@ import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDoma
|
||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||
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.data.EnumOptionData;
|
||||
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.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.organisation.monetary.service.CurrencyReadPlatformService;
|
||||
@@ -71,7 +74,9 @@ import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYea
|
||||
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||
import org.apache.fineract.portfolio.currentproduct.service.CurrentProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.currentproduct.service.CurrentProductWritePlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsDropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
@@ -101,6 +106,9 @@ public class CurrentProductsApiResource {
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final CodeValueReadPlatformService codeValueReadPlatformService;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final CurrentProductWritePlatformService currentProductWritePlatformService;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final DefaultToApiJsonSerializer<SavingsProductTempData> tempDataApiJsonSerializer;
|
||||
|
||||
|
||||
@POST
|
||||
@@ -122,9 +130,72 @@ public class CurrentProductsApiResource {
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String update(@PathParam("productId") final Long productId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateCurrentProduct(productId).withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
// Bypasses the native command-source/maker-checker pipeline by design: edits are held in
|
||||
// m_savings_product_temp (a pending row there is itself the lock) and only applied to m_current_product on
|
||||
// approval - see /currentproducts/{productId}/approve and /reject below.
|
||||
this.context.authenticatedUser().validateHasUpdatePermission(DepositsApiConstants.CURRENT_PRODUCT_RESOURCE_NAME);
|
||||
|
||||
final JsonCommand command = JsonCommand.fromJsonElement(productId, this.fromApiJsonHelper.parse(apiRequestBodyAsJson),
|
||||
this.fromApiJsonHelper);
|
||||
|
||||
final CommandProcessingResult result = this.currentProductWritePlatformService.submitEditForApproval(productId, command);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("pending")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List Current Products pending edit approval")
|
||||
public String retrieveAllPending(@Context final UriInfo uriInfo) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_PRODUCT_RESOURCE_NAME);
|
||||
final Collection<SavingsProductTempData> pendingEdits = this.savingProductReadPlatformService.retrieveAllPendingEdits();
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.tempDataApiJsonSerializer.serialize(settings, pendingEdits);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{productId}/pending")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve the pending edit for a Current Product")
|
||||
public String retrievePending(@PathParam("productId") final Long productId, @Context final UriInfo uriInfo) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_PRODUCT_RESOURCE_NAME);
|
||||
final SavingsProductTempData pendingEdit = this.savingProductReadPlatformService.retrievePendingEdit(productId);
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.tempDataApiJsonSerializer.serialize(settings, pendingEdit);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{productId}/approve")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Approve a pending Current Product edit")
|
||||
public String approvePending(@PathParam("productId") final Long productId) {
|
||||
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("APPROVE_CURRENTPRODUCT")) {
|
||||
throw new NoAuthorizationException("User has no authority to approve current products");
|
||||
}
|
||||
final CommandProcessingResult result = this.currentProductWritePlatformService.approveEdit(productId);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{productId}/reject")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Reject a pending Current Product edit")
|
||||
public String rejectPending(@PathParam("productId") final Long productId, @Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("REJECT_CURRENTPRODUCT")) {
|
||||
throw new NoAuthorizationException("User has no authority to reject current products");
|
||||
}
|
||||
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.currentProductWritePlatformService.rejectEdit(productId, rejectReason);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
|
||||
+77
-3
@@ -53,14 +53,17 @@ import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import java.util.Locale;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||
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.codes.data.CodeValueData;
|
||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||
import org.apache.fineract.infrastructure.security.exception.NoAuthorizationException;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.organisation.monetary.service.CurrencyReadPlatformService;
|
||||
@@ -78,8 +81,10 @@ import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYea
|
||||
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
|
||||
import org.apache.fineract.portfolio.savings.data.FixedDepositProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||
import org.apache.fineract.portfolio.savings.service.DepositProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.DepositsDropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.FixedDepositProductWritePlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsDropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
@@ -116,6 +121,9 @@ public class FixedDepositProductsApiResource {
|
||||
private final TaxReadPlatformService taxReadPlatformService;
|
||||
private final CodeValueReadPlatformService codeValueReadPlatformService;
|
||||
private final org.springframework.jdbc.core.JdbcTemplate jdbcTemplate;
|
||||
private final FixedDepositProductWritePlatformService fixedDepositProductWritePlatformService;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final DefaultToApiJsonSerializer<SavingsProductTempData> tempDataApiJsonSerializer;
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@@ -151,15 +159,81 @@ public class FixedDepositProductsApiResource {
|
||||
public String update(@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateFixedDepositProduct(productId)
|
||||
.withJson(apiRequestBodyAsJson).build();
|
||||
// Bypasses the native command-source/maker-checker pipeline by design: edits are held in
|
||||
// m_savings_product_temp (a pending row there is itself the lock) and only applied to m_savings_product on
|
||||
// approval - see /fixeddepositproducts/{productId}/approve and /reject below.
|
||||
this.context.authenticatedUser().validateHasUpdatePermission(DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME);
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
final JsonCommand command = JsonCommand.fromJsonElement(productId, this.fromApiJsonHelper.parse(apiRequestBodyAsJson),
|
||||
this.fromApiJsonHelper);
|
||||
|
||||
final CommandProcessingResult result = this.fixedDepositProductWritePlatformService.submitEditForApproval(productId, command);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("pending")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List Fixed Deposit Products pending edit approval")
|
||||
public String retrieveAllPending(@Context final UriInfo uriInfo) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME);
|
||||
final Collection<SavingsProductTempData> pendingEdits = this.depositProductReadPlatformService
|
||||
.retrieveAllPendingEdits(DepositAccountType.FIXED_DEPOSIT);
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.tempDataApiJsonSerializer.serialize(settings, pendingEdits);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{productId}/pending")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve the pending edit for a Fixed Deposit Product")
|
||||
public String retrievePending(@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Context final UriInfo uriInfo) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME);
|
||||
final SavingsProductTempData pendingEdit = this.depositProductReadPlatformService
|
||||
.retrievePendingEdit(DepositAccountType.FIXED_DEPOSIT, productId);
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.tempDataApiJsonSerializer.serialize(settings, pendingEdit);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{productId}/approve")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Approve a pending Fixed Deposit Product edit")
|
||||
public String approvePending(@PathParam("productId") @Parameter(description = "productId") final Long productId) {
|
||||
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("APPROVE_FIXEDDEPOSITPRODUCT")) {
|
||||
throw new NoAuthorizationException("User has no authority to approve fixed deposit products");
|
||||
}
|
||||
final CommandProcessingResult result = this.fixedDepositProductWritePlatformService.approveEdit(productId);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{productId}/reject")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Reject a pending Fixed Deposit Product edit")
|
||||
public String rejectPending(@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("REJECT_FIXEDDEPOSITPRODUCT")) {
|
||||
throw new NoAuthorizationException("User has no authority to reject fixed deposit products");
|
||||
}
|
||||
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.fixedDepositProductWritePlatformService.rejectEdit(productId, rejectReason);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
|
||||
+78
-3
@@ -55,10 +55,13 @@ import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.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.data.EnumOptionData;
|
||||
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.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.organisation.monetary.service.CurrencyReadPlatformService;
|
||||
@@ -85,8 +88,10 @@ import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import java.time.LocalDate;
|
||||
import org.apache.fineract.portfolio.savings.data.RecurringDepositProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||
import org.apache.fineract.portfolio.savings.service.DepositProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.DepositsDropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.RecurringDepositProductWritePlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsDropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
@@ -126,6 +131,9 @@ public class RecurringDepositProductsApiResource {
|
||||
.registerTypeAdapter(LocalDate.class,
|
||||
(JsonSerializer<LocalDate>) (src, typeOfSrc, ctx) -> new JsonPrimitive(src.toString()))
|
||||
.create();
|
||||
private final RecurringDepositProductWritePlatformService recurringDepositProductWritePlatformService;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final DefaultToApiJsonSerializer<SavingsProductTempData> tempDataApiJsonSerializer;
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@@ -158,15 +166,82 @@ public class RecurringDepositProductsApiResource {
|
||||
public String update(@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateRecurringDepositProduct(productId)
|
||||
.withJson(apiRequestBodyAsJson).build();
|
||||
// Bypasses the native command-source/maker-checker pipeline by design: edits are held in
|
||||
// m_savings_product_temp (a pending row there is itself the lock) and only applied to m_savings_product on
|
||||
// approval - see /recurringdepositproducts/{productId}/approve and /reject below.
|
||||
this.context.authenticatedUser().validateHasUpdatePermission(DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME);
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
final JsonCommand command = JsonCommand.fromJsonElement(productId, this.fromApiJsonHelper.parse(apiRequestBodyAsJson),
|
||||
this.fromApiJsonHelper);
|
||||
|
||||
final CommandProcessingResult result = this.recurringDepositProductWritePlatformService.submitEditForApproval(productId,
|
||||
command);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("pending")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List Recurring Deposit Products pending edit approval")
|
||||
public String retrieveAllPending(@Context final UriInfo uriInfo) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME);
|
||||
final Collection<SavingsProductTempData> pendingEdits = this.depositProductReadPlatformService
|
||||
.retrieveAllPendingEdits(DepositAccountType.RECURRING_DEPOSIT);
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.tempDataApiJsonSerializer.serialize(settings, pendingEdits);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{productId}/pending")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve the pending edit for a Recurring Deposit Product")
|
||||
public String retrievePending(@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Context final UriInfo uriInfo) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME);
|
||||
final SavingsProductTempData pendingEdit = this.depositProductReadPlatformService
|
||||
.retrievePendingEdit(DepositAccountType.RECURRING_DEPOSIT, productId);
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.tempDataApiJsonSerializer.serialize(settings, pendingEdit);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{productId}/approve")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Approve a pending Recurring Deposit Product edit")
|
||||
public String approvePending(@PathParam("productId") @Parameter(description = "productId") final Long productId) {
|
||||
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("APPROVE_RECURRINGDEPOSITPRODUCT")) {
|
||||
throw new NoAuthorizationException("User has no authority to approve recurring deposit products");
|
||||
}
|
||||
final CommandProcessingResult result = this.recurringDepositProductWritePlatformService.approveEdit(productId);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{productId}/reject")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Reject a pending Recurring Deposit Product edit")
|
||||
public String rejectPending(@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("REJECT_RECURRINGDEPOSITPRODUCT")) {
|
||||
throw new NoAuthorizationException("User has no authority to reject recurring deposit products");
|
||||
}
|
||||
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.recurringDepositProductWritePlatformService.rejectEdit(productId, rejectReason);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
|
||||
+88
-3
@@ -59,8 +59,11 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
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.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.organisation.monetary.service.CurrencyReadPlatformService;
|
||||
@@ -74,9 +77,11 @@ import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYea
|
||||
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsDropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsProductWritePlatformService;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.apache.fineract.portfolio.tax.service.TaxReadPlatformService;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
@@ -105,6 +110,9 @@ public class SavingsProductsApiResource {
|
||||
private final TaxReadPlatformService taxReadPlatformService;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final SavingsProductWritePlatformService savingsProductWritePlatformService;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final DefaultToApiJsonSerializer<SavingsProductTempData> tempDataApiJsonSerializer;
|
||||
|
||||
|
||||
@POST
|
||||
@@ -135,15 +143,92 @@ public class SavingsProductsApiResource {
|
||||
public String update(@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateSavingProduct(productId).withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
// Bypasses the native command-source/maker-checker pipeline by design: edits are held in
|
||||
// m_savings_product_temp (a pending row there is itself the lock) and only applied to m_savings_product on
|
||||
// approval - see /savingsproducts/{productId}/approve and /reject below.
|
||||
this.context.authenticatedUser().validateHasUpdatePermission(SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME);
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
final JsonCommand command = JsonCommand.fromJsonElement(productId, this.fromApiJsonHelper.parse(apiRequestBodyAsJson),
|
||||
this.fromApiJsonHelper);
|
||||
|
||||
final CommandProcessingResult result = this.savingsProductWritePlatformService.submitEditForApproval(productId, command);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("pending")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List Savings Products pending edit approval", description = "Lists all savings product edits currently awaiting checker approval")
|
||||
public String retrieveAllPending(@Context final UriInfo uriInfo) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME);
|
||||
|
||||
final Collection<SavingsProductTempData> pendingEdits = this.savingProductReadPlatformService.retrieveAllPendingEdits();
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.tempDataApiJsonSerializer.serialize(settings, pendingEdits);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{productId}/pending")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve the pending edit for a Savings Product", description = "Retrieves the draft awaiting checker approval for this product, if any")
|
||||
public String retrievePending(@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Context final UriInfo uriInfo) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME);
|
||||
|
||||
final SavingsProductTempData pendingEdit = this.savingProductReadPlatformService.retrievePendingEdit(productId);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.tempDataApiJsonSerializer.serialize(settings, pendingEdit);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{productId}/approve")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Approve a pending Savings Product edit", description = "Replays the pending edit and applies it to the Savings Product. The maker of the edit cannot also approve it.")
|
||||
public String approvePending(@PathParam("productId") @Parameter(description = "productId") final Long productId) {
|
||||
|
||||
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("APPROVE_SAVINGSPRODUCT")) {
|
||||
throw new NoAuthorizationException("User has no authority to approve savings products");
|
||||
}
|
||||
|
||||
final CommandProcessingResult result = this.savingsProductWritePlatformService.approveEdit(productId);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{productId}/reject")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Reject a pending Savings Product edit", description = "Discards the pending edit without applying it. The maker of the edit cannot also reject it.")
|
||||
public String rejectPending(@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("REJECT_SAVINGSPRODUCT")) {
|
||||
throw new NoAuthorizationException("User has no authority to reject savings products");
|
||||
}
|
||||
|
||||
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.savingsProductWritePlatformService.rejectEdit(productId, rejectReason);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
|
||||
+11
@@ -25,6 +25,7 @@ import jakarta.persistence.Embedded;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.OneToOne;
|
||||
import jakarta.persistence.PostLoad;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
@@ -135,7 +136,17 @@ public class DepositAccountRecurringDetail extends AbstractPersistableCustom<Lon
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
@PostLoad
|
||||
public void postLoad() {
|
||||
if (this.recurringDetail == null) {
|
||||
this.recurringDetail = new DepositRecurringDetail();
|
||||
}
|
||||
}
|
||||
|
||||
public DepositRecurringDetail recurringDetail() {
|
||||
if (this.recurringDetail == null) {
|
||||
this.recurringDetail = new DepositRecurringDetail();
|
||||
}
|
||||
return this.recurringDetail;
|
||||
}
|
||||
|
||||
|
||||
+12
@@ -42,6 +42,8 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.specialC
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.weakerSectionParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominationFacilityParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.autoDebitFacilityParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.introducerRequiredParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shareLinkingEnabledParamName;
|
||||
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.isSeniorCitizenParamName;
|
||||
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.seniorCitizenInterestRateParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.allowOverdraftParamName;
|
||||
@@ -395,6 +397,16 @@ public class SavingsAccountAssembler {
|
||||
if (command.parameterExists(autoDebitFacilityParamName)) {
|
||||
account.setAutoDebitFacility(this.fromApiJsonHelper.extractBooleanNamed(autoDebitFacilityParamName, element));
|
||||
}
|
||||
if (command.parameterExists(introducerRequiredParamName)) {
|
||||
account.setIntroducerRequired(this.fromApiJsonHelper.extractBooleanNamed(introducerRequiredParamName, element));
|
||||
} else if (account.savingsProduct() != null) {
|
||||
account.setIntroducerRequired(account.savingsProduct().isIntroducerRequired());
|
||||
}
|
||||
if (command.parameterExists(shareLinkingEnabledParamName)) {
|
||||
account.setShareLinkingEnabled(this.fromApiJsonHelper.extractBooleanNamed(shareLinkingEnabledParamName, element));
|
||||
} else if (account.savingsProduct() != null) {
|
||||
account.setShareLinkingEnabled(account.savingsProduct().isShareLinkingEnabled());
|
||||
}
|
||||
|
||||
// Senior citizen rate — runs after validateDomainRules so it is the final persisted value
|
||||
if (command.parameterExists(isSeniorCitizenParamName)) {
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.handler;
|
||||
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "TERMINATE")
|
||||
public class CurrentAccountTerminateCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final CurrentAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CurrentAccountTerminateCommandHandler(final CurrentAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.terminateAccount(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.handler;
|
||||
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "FIXEDDEPOSITACCOUNT", action = "TERMINATE")
|
||||
public class FixedDepositAccountTerminateCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public FixedDepositAccountTerminateCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.terminate(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.handler;
|
||||
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "RECURRINGDEPOSITACCOUNT", action = "TERMINATE")
|
||||
public class RecurringDepositAccountTerminateCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public RecurringDepositAccountTerminateCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.terminate(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.handler;
|
||||
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "SAVINGSACCOUNT", action = "TERMINATE")
|
||||
public class SavingsAccountTerminateCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public SavingsAccountTerminateCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.terminate(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "ACTIVATE")
|
||||
public class ActivateCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
private final CurrentAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public ActivateCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
public ActivateCurrentAccountCommandHandler(final CurrentAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.activate(command.getSavingsId(), command);
|
||||
return this.writePlatformService.activate(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "CLOSE")
|
||||
public class CloseSavingsCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
private final CurrentAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CloseSavingsCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
public CloseSavingsCurrentAccountCommandHandler(final CurrentAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.close(command.getSavingsId(), command);
|
||||
return this.writePlatformService.closeAccount(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsApplicationProcessWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "APPROVE")
|
||||
public class CurrentAccountApplicationApprovalCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsApplicationProcessWritePlatformService applicationProcessService;
|
||||
private final CurrentAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CurrentAccountApplicationApprovalCommandHandler(final SavingsApplicationProcessWritePlatformService applicationProcessService) {
|
||||
this.applicationProcessService = applicationProcessService;
|
||||
public CurrentAccountApplicationApprovalCommandHandler(final CurrentAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.applicationProcessService.approveApplication(command.getSavingsId(), command);
|
||||
return this.writePlatformService.approveApplication(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsApplicationProcessWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "APPROVALUNDO")
|
||||
public class CurrentAccountApplicationApprovalUndoCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsApplicationProcessWritePlatformService applicationProcessService;
|
||||
private final CurrentAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CurrentAccountApplicationApprovalUndoCommandHandler(final SavingsApplicationProcessWritePlatformService applicationProcessService) {
|
||||
this.applicationProcessService = applicationProcessService;
|
||||
public CurrentAccountApplicationApprovalUndoCommandHandler(final CurrentAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.applicationProcessService.undoApplicationApproval(command.getSavingsId(), command);
|
||||
return this.writePlatformService.undoApproval(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsApplicationProcessWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "DELETE")
|
||||
public class CurrentAccountApplicationDeletionCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsApplicationProcessWritePlatformService applicationProcessService;
|
||||
private final CurrentAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CurrentAccountApplicationDeletionCommandHandler(final SavingsApplicationProcessWritePlatformService applicationProcessService) {
|
||||
this.applicationProcessService = applicationProcessService;
|
||||
public CurrentAccountApplicationDeletionCommandHandler(final CurrentAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.applicationProcessService.deleteApplication(command.getSavingsId());
|
||||
return this.writePlatformService.deleteApplication(command.entityId());
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsApplicationProcessWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "UPDATE")
|
||||
public class CurrentAccountApplicationModificationCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsApplicationProcessWritePlatformService applicationProcessService;
|
||||
private final CurrentAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CurrentAccountApplicationModificationCommandHandler(final SavingsApplicationProcessWritePlatformService applicationProcessService) {
|
||||
this.applicationProcessService = applicationProcessService;
|
||||
public CurrentAccountApplicationModificationCommandHandler(final CurrentAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.applicationProcessService.modifyApplication(command.getSavingsId(), command);
|
||||
return this.writePlatformService.modifyApplication(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsApplicationProcessWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "REJECT")
|
||||
public class CurrentAccountApplicationRejectedCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsApplicationProcessWritePlatformService applicationProcessService;
|
||||
private final CurrentAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CurrentAccountApplicationRejectedCommandHandler(final SavingsApplicationProcessWritePlatformService applicationProcessService) {
|
||||
this.applicationProcessService = applicationProcessService;
|
||||
public CurrentAccountApplicationRejectedCommandHandler(final CurrentAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.applicationProcessService.rejectApplication(command.getSavingsId(), command);
|
||||
return this.writePlatformService.rejectApplication(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsApplicationProcessWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "CREATE")
|
||||
public class CurrentAccountApplicationSubmittalCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsApplicationProcessWritePlatformService applicationProcessService;
|
||||
private final CurrentAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CurrentAccountApplicationSubmittalCommandHandler(final SavingsApplicationProcessWritePlatformService applicationProcessService) {
|
||||
this.applicationProcessService = applicationProcessService;
|
||||
public CurrentAccountApplicationSubmittalCommandHandler(final CurrentAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.applicationProcessService.submitApplication(command);
|
||||
return this.writePlatformService.submitApplication(command);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsApplicationProcessWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "WITHDRAW")
|
||||
public class CurrentAccountApplicationWithdrawnCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsApplicationProcessWritePlatformService applicationProcessService;
|
||||
private final CurrentAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CurrentAccountApplicationWithdrawnCommandHandler(final SavingsApplicationProcessWritePlatformService applicationProcessService) {
|
||||
this.applicationProcessService = applicationProcessService;
|
||||
public CurrentAccountApplicationWithdrawnCommandHandler(final CurrentAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.applicationProcessService.applicantWithdrawsFromApplication(command.getSavingsId(), command);
|
||||
return this.writePlatformService.withdrawApplication(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
|
||||
+31
-4
@@ -297,12 +297,21 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead
|
||||
hasWhere = true;
|
||||
}
|
||||
if (StringUtils.isNotBlank(status)) {
|
||||
final Integer statusEnum = resolveStatusEnum(status);
|
||||
if (statusEnum != null) {
|
||||
if ("terminated".equalsIgnoreCase(status)) {
|
||||
sqlBuilder.append(hasWhere ? " AND " : " WHERE ");
|
||||
sqlBuilder.append("ca.status_enum = ?");
|
||||
params.add(statusEnum);
|
||||
sqlBuilder.append("ca.terminate_true = true");
|
||||
hasWhere = true;
|
||||
} else {
|
||||
final Integer statusEnum = resolveStatusEnum(status);
|
||||
if (statusEnum != null) {
|
||||
sqlBuilder.append(hasWhere ? " AND " : " WHERE ");
|
||||
sqlBuilder.append("ca.status_enum = ?");
|
||||
params.add(statusEnum);
|
||||
hasWhere = true;
|
||||
if (statusEnum == 100) {
|
||||
sqlBuilder.append(" AND (ca.terminate_true = false OR ca.terminate_true IS NULL)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fromDate != null) {
|
||||
@@ -356,6 +365,7 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead
|
||||
case "withdrawn" -> 400;
|
||||
case "rejected" -> 500;
|
||||
case "closed" -> 600;
|
||||
case "terminated" -> 900;
|
||||
default -> {
|
||||
try {
|
||||
yield Integer.parseInt(status);
|
||||
@@ -389,6 +399,7 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead
|
||||
// status
|
||||
int statusEnum = toInt(row.get("status_enum"), 100);
|
||||
out.put("status", buildStatus(statusEnum));
|
||||
out.put("terminateTrue", toBoolean(row.get("terminate_true")));
|
||||
|
||||
// subStatus
|
||||
int subStatusEnum = toInt(row.get("sub_status_enum"), 0);
|
||||
@@ -443,6 +454,7 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead
|
||||
out.put("msme", row.get("msme"));
|
||||
out.put("autoDebitFacility", row.get("auto_debit_facility"));
|
||||
out.put("balanceSwapOption", row.get("balance_swap_option"));
|
||||
out.put("shareLinkingEnabled", row.get("is_linking_enable"));
|
||||
out.put("establishmentDate", row.get("establishment_date"));
|
||||
out.put("startOperationDate", row.get("start_operation_date"));
|
||||
out.put("nonIndividualClient", row.get("is_non_individual_client"));
|
||||
@@ -501,6 +513,13 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead
|
||||
s.put("approved", false); s.put("active", false);
|
||||
s.put("rejected", false); s.put("withdrawnByApplicant", false); s.put("closed", true);
|
||||
}
|
||||
case 900 -> {
|
||||
s.put("code", "savingsAccountStatusType.terminated");
|
||||
s.put("value", "Terminated");
|
||||
s.put("submittedAndPendingApproval", false);
|
||||
s.put("approved", false); s.put("active", false);
|
||||
s.put("rejected", false); s.put("withdrawnByApplicant", false); s.put("closed", false);
|
||||
}
|
||||
default -> {
|
||||
s.put("code", "savingsAccountStatusType.invalid");
|
||||
s.put("value", "Invalid");
|
||||
@@ -658,6 +677,14 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead
|
||||
try { return new BigDecimal(val.toString().trim()); } catch (Exception e) { return null; }
|
||||
}
|
||||
|
||||
private boolean toBoolean(Object val) {
|
||||
if (val == null) return false;
|
||||
if (val instanceof Boolean) return (Boolean) val;
|
||||
if (val instanceof Number) return ((Number) val).intValue() == 1;
|
||||
String s = val.toString().trim();
|
||||
return "true".equalsIgnoreCase(s) || "1".equals(s);
|
||||
}
|
||||
|
||||
private static class FlatRowMapper implements RowMapper<Map<String, Object>> {
|
||||
@Override
|
||||
public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
|
||||
+5
-5
@@ -30,7 +30,7 @@ import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||
import org.apache.fineract.portfolio.account.service.AccountNumberGenerator;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountNumberGenerator;
|
||||
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.DatatableData;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
|
||||
@@ -80,7 +80,7 @@ public class CurrentAccountTemplateReadPlatformServiceImpl implements CurrentAcc
|
||||
private final SavingsDropdownReadPlatformService dropdownReadPlatformService;
|
||||
private final ChargeReadPlatformService chargeReadPlatformService;
|
||||
private final EntityDatatableChecksReadService entityDatatableChecksReadService;
|
||||
private final AccountNumberGenerator accountNumberGenerator;
|
||||
private final CurrentAccountNumberGenerator currentAccountNumberGenerator;
|
||||
private final CodeValueReadPlatformService codeValueReadPlatformService;
|
||||
|
||||
public CurrentAccountTemplateReadPlatformServiceImpl(final PlatformSecurityContext context, final JdbcTemplate jdbcTemplate,
|
||||
@@ -91,7 +91,7 @@ public class CurrentAccountTemplateReadPlatformServiceImpl implements CurrentAcc
|
||||
final ChargeReadPlatformService chargeReadPlatformService,
|
||||
final EntityDatatableChecksReadService entityDatatableChecksReadService,
|
||||
final ColumnValidator columnValidator,
|
||||
final AccountNumberGenerator accountNumberGenerator,
|
||||
final CurrentAccountNumberGenerator currentAccountNumberGenerator,
|
||||
final CodeValueReadPlatformService codeValueReadPlatformService) {
|
||||
this.context = context;
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
@@ -102,7 +102,7 @@ public class CurrentAccountTemplateReadPlatformServiceImpl implements CurrentAcc
|
||||
this.dropdownReadPlatformService = dropdownReadPlatformService;
|
||||
this.chargeReadPlatformService = chargeReadPlatformService;
|
||||
this.entityDatatableChecksReadService = entityDatatableChecksReadService;
|
||||
this.accountNumberGenerator = accountNumberGenerator;
|
||||
this.currentAccountNumberGenerator = currentAccountNumberGenerator;
|
||||
this.codeValueReadPlatformService = codeValueReadPlatformService;
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ public class CurrentAccountTemplateReadPlatformServiceImpl implements CurrentAcc
|
||||
productData.getOdAccountClosedZeroBalanceInDayClosing() != null && productData.getOdAccountClosedZeroBalanceInDayClosing());
|
||||
// Generate preview account number using current product's own GL account ID
|
||||
try {
|
||||
final String preview = accountNumberGenerator.previewAccountNumberByGlAccountId(productData.getGlAccountId(), officeId);
|
||||
final String preview = currentAccountNumberGenerator.preview(productId, officeId);
|
||||
template.setPreviewAccountNo(preview);
|
||||
} catch (final Exception ex) {
|
||||
log.warn("Failed to generate preview account number for productId={}: {}", productId, ex.getMessage());
|
||||
|
||||
+64
-11
@@ -45,6 +45,7 @@ import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWriteP
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountNumberGenerator;
|
||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProduct;
|
||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductRepository;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForSavings;
|
||||
import org.apache.fineract.accounting.common.AccountingRuleType;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||
@@ -127,6 +128,7 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
boolean balanceSwapOption = json.has("balanceSwapOption") && !json.get("balanceSwapOption").isJsonNull()
|
||||
? json.get("balanceSwapOption").getAsBoolean() : product.isBalanceSwapOption();
|
||||
|
||||
final AppUser user = context.authenticatedUser();
|
||||
final CurrentAccount account = CurrentAccount.createNew(
|
||||
client, productId, office, null, "TEMP", submittedOnDate, accountTypeEnum,
|
||||
product.currency().getCode(), product.currency().getDigitsAfterDecimal(),
|
||||
@@ -139,6 +141,11 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
product.getCustomerSegment(), product.isAutoPost(),
|
||||
product.getInterestCalculateSelector(), product.getAveragePeriod(),
|
||||
product.getAvgBalanceCalcPeriod());
|
||||
account.setSubmittedOnByUser(user);
|
||||
|
||||
boolean shareLinkingEnabled = json.has("shareLinkingEnabled") && !json.get("shareLinkingEnabled").isJsonNull()
|
||||
? json.get("shareLinkingEnabled").getAsBoolean() : product.isShareLinkingEnabled();
|
||||
account.setShareLinkingEnabled(shareLinkingEnabled);
|
||||
|
||||
// Save CBS account type (descriptive name: "Standard Account", "Basic Account", etc.)
|
||||
if (cbsAccountType != null) account.setCbsAccountType(cbsAccountType);
|
||||
@@ -244,6 +251,9 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
throw e;
|
||||
}
|
||||
|
||||
log.info("Current Account created successfully: [id={}, accountNo={}, clientId={}, officeId={}, submittedOnDate={}]",
|
||||
account.getId(), account.getAccountNo(), clientId, office.getId(), submittedOnDate);
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withEntityId(account.getId())
|
||||
.withClientId(clientId)
|
||||
@@ -254,12 +264,12 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult approveApplication(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
final AppUser user = context.authenticatedUser();
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
final JsonObject json = command.parsedJson().getAsJsonObject();
|
||||
LocalDate approvedOnDate = parseDate(getString(json, "approvedOnDate"), getString(json, "dateFormat"), getString(json, "locale"));
|
||||
if (approvedOnDate == null) approvedOnDate = LocalDate.now();
|
||||
account.approve(approvedOnDate);
|
||||
account.approve(approvedOnDate, user);
|
||||
currentAccountRepository.saveAndFlush(account);
|
||||
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
|
||||
}
|
||||
@@ -271,6 +281,7 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
account.setStatusEnum(100);
|
||||
account.setApprovedOnDate(null);
|
||||
account.setApprovedOnByUser(null);
|
||||
currentAccountRepository.saveAndFlush(account);
|
||||
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
|
||||
}
|
||||
@@ -278,7 +289,7 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult activate(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
final AppUser user = context.authenticatedUser();
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
final JsonObject json = command.parsedJson().getAsJsonObject();
|
||||
LocalDate activatedOnDate = parseDate(getString(json, "activatedOnDate"), getString(json, "dateFormat"), getString(json, "locale"));
|
||||
@@ -299,7 +310,7 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
openingBalanceValue, minRequired)));
|
||||
}
|
||||
|
||||
account.activate(activatedOnDate);
|
||||
account.activate(activatedOnDate, user);
|
||||
applyFacilityChargesOnActivation(account);
|
||||
|
||||
java.math.BigDecimal currentBalance = java.math.BigDecimal.ZERO;
|
||||
@@ -356,12 +367,12 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult rejectApplication(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
final AppUser user = context.authenticatedUser();
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
final JsonObject json = command.parsedJson().getAsJsonObject();
|
||||
LocalDate rejectedOnDate = parseDate(getString(json, "rejectedOnDate"), getString(json, "dateFormat"), getString(json, "locale"));
|
||||
if (rejectedOnDate == null) rejectedOnDate = LocalDate.now();
|
||||
account.reject(rejectedOnDate);
|
||||
account.reject(rejectedOnDate, user);
|
||||
currentAccountRepository.saveAndFlush(account);
|
||||
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
|
||||
}
|
||||
@@ -369,12 +380,12 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult withdrawApplication(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
final AppUser user = context.authenticatedUser();
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
final JsonObject json = command.parsedJson().getAsJsonObject();
|
||||
LocalDate withdrawnOnDate = parseDate(getString(json, "withdrawnOnDate"), getString(json, "dateFormat"), getString(json, "locale"));
|
||||
if (withdrawnOnDate == null) withdrawnOnDate = LocalDate.now();
|
||||
account.withdraw(withdrawnOnDate);
|
||||
account.withdraw(withdrawnOnDate, user);
|
||||
currentAccountRepository.saveAndFlush(account);
|
||||
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
|
||||
}
|
||||
@@ -382,12 +393,12 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult closeAccount(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
final AppUser user = context.authenticatedUser();
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
final JsonObject json = command.parsedJson().getAsJsonObject();
|
||||
LocalDate closedOnDate = parseDate(getString(json, "closedOnDate"), getString(json, "dateFormat"), getString(json, "locale"));
|
||||
if (closedOnDate == null) closedOnDate = LocalDate.now();
|
||||
account.close(closedOnDate);
|
||||
account.close(closedOnDate, user);
|
||||
currentAccountRepository.saveAndFlush(account);
|
||||
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
|
||||
}
|
||||
@@ -399,6 +410,19 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
final JsonObject json = command.parsedJson().getAsJsonObject();
|
||||
|
||||
if (account.getStatusEnum() != null) {
|
||||
if (account.getStatusEnum() == 500) {
|
||||
account.setStatusEnum(100);
|
||||
account.setRejectedOnDate(null);
|
||||
account.setRejectedOnByUser(null);
|
||||
} else if (account.getStatusEnum() == 400) {
|
||||
account.setStatusEnum(100);
|
||||
account.setWithdrawnOnDate(null);
|
||||
account.setWithdrawnOnByUser(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (json.has("externalId") && !json.get("externalId").isJsonNull())
|
||||
account.setExternalId(json.get("externalId").getAsString());
|
||||
if (json.has("accountStatus") && !json.get("accountStatus").isJsonNull())
|
||||
@@ -422,6 +446,8 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
account.setBalanceSwapOption(json.get("balanceSwapOption").getAsBoolean());
|
||||
if (json.has("autoDebitFacility") && !json.get("autoDebitFacility").isJsonNull())
|
||||
account.setAutoDebitFacility(json.get("autoDebitFacility").getAsBoolean());
|
||||
if (json.has("shareLinkingEnabled") && !json.get("shareLinkingEnabled").isJsonNull())
|
||||
account.setShareLinkingEnabled(json.get("shareLinkingEnabled").getAsBoolean());
|
||||
if (json.has("introducerClientId") && !json.get("introducerClientId").isJsonNull())
|
||||
account.setIntroducerClientId(json.get("introducerClientId").getAsLong());
|
||||
if (json.has("introducerName") && !json.get("introducerName").isJsonNull())
|
||||
@@ -484,6 +510,30 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult terminateAccount(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
account.terminate();
|
||||
currentAccountRepository.saveAndFlush(account);
|
||||
|
||||
final java.util.Map<String, Object> changes = new java.util.LinkedHashMap<>();
|
||||
changes.put("terminateTrue", true);
|
||||
changes.put("status", 900);
|
||||
|
||||
final Long officeId = account.getOffice() != null ? account.getOffice().getId() : null;
|
||||
final Long clientId = account.getClient() != null ? account.getClient().getId() : null;
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withEntityId(accountId)
|
||||
.withOfficeId(officeId)
|
||||
.withClientId(clientId)
|
||||
.withSavingsId(accountId)
|
||||
.with(changes)
|
||||
.build();
|
||||
}
|
||||
|
||||
// ─── helpers ──────────────────────────────────────────────────────────────
|
||||
|
||||
private CurrentAccount findAccount(Long accountId) {
|
||||
@@ -492,7 +542,10 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
}
|
||||
|
||||
private CurrentAccountFacilityConfig assembleFacilityConfig(CurrentAccount account, JsonObject fcJson) {
|
||||
final CurrentAccountFacilityConfig cfg = CurrentAccountFacilityConfig.createFor(account);
|
||||
CurrentAccountFacilityConfig cfg = account.getFacilityConfig();
|
||||
if (cfg == null) {
|
||||
cfg = CurrentAccountFacilityConfig.createFor(account);
|
||||
}
|
||||
cfg.setAtmFacility(getBool(fcJson, "atmFacility"));
|
||||
cfg.setAtmWithdrawalFacility(getBool(fcJson, "atmWithdrawalFacility"));
|
||||
cfg.setUpiFacility(getBool(fcJson, "upiFacility"));
|
||||
|
||||
+87
-2
@@ -18,10 +18,13 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.service;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.accounting.common.AccountingEnumerations;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
@@ -34,11 +37,20 @@ import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.data.DepositProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.FixedDepositProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.RecurringDepositProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||
import org.apache.fineract.portfolio.savings.exception.FixedDepositProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductNoPendingEditException;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEdit;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFieldMapper;
|
||||
import java.util.Set;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class DepositProductReadPlatformServiceImpl implements DepositProductReadPlatformService {
|
||||
@@ -49,6 +61,10 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
|
||||
private final PlatformSecurityContext context;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final InterestRateChartReadPlatformService chartReadPlatformService;
|
||||
private final ProductPendingEditRepository productPendingEditRepository;
|
||||
private final ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository;
|
||||
private final ProductPendingEditChargeRepository pendingEditChargeRepository;
|
||||
private final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository;
|
||||
|
||||
@Override
|
||||
public Collection<DepositProductData> retrieveAll(final DepositAccountType depositAccountType) {
|
||||
@@ -124,6 +140,69 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final Gson GSON = new Gson();
|
||||
|
||||
private static String productPendingType(final DepositAccountType depositAccountType) {
|
||||
return depositAccountType.isFixedDeposit() ? "FD" : "RD";
|
||||
}
|
||||
|
||||
private RowMapper<SavingsProductTempData> getPendingEditRowMapper() {
|
||||
return (rs, rowNum) -> {
|
||||
final SavingsProductTempData data = new SavingsProductTempData();
|
||||
data.setId(rs.getLong("tempId"));
|
||||
data.setSavingsProductId(rs.getLong("savingsProductId"));
|
||||
data.setSavingsProductName(rs.getString("savingsProductName"));
|
||||
data.setMakerId(rs.getLong("makerId"));
|
||||
data.setMakerUsername(rs.getString("makerUsername"));
|
||||
data.setCheckerId(JdbcSupport.getLong(rs, "checkerId"));
|
||||
data.setCheckerUsername(rs.getString("checkerUsername"));
|
||||
data.setCreatedOn(rs.getTimestamp("createdOn") == null ? null : rs.getTimestamp("createdOn").toLocalDateTime());
|
||||
data.setCheckedOn(rs.getTimestamp("checkedOn") == null ? null : rs.getTimestamp("checkedOn").toLocalDateTime());
|
||||
data.setStatus(rs.getString("status"));
|
||||
data.setRejectReason(rs.getString("rejectReason"));
|
||||
|
||||
final Long tempId = data.getId();
|
||||
final java.util.Optional<ProductPendingEdit> opt = this.productPendingEditRepository.findById(tempId);
|
||||
if (opt.isPresent()) {
|
||||
final ProductPendingEdit pendingEdit = opt.get();
|
||||
final Set<String> submittedFields = new java.util.HashSet<>(
|
||||
this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId()));
|
||||
final Map<String, Object> proposedChanges = ProductPendingEditFieldMapper.reconstructProposedChangesMap(
|
||||
pendingEdit, submittedFields, this.pendingEditChargeRepository, this.pendingEditGlMappingRepository, null, GSON);
|
||||
data.setProposedChanges(proposedChanges);
|
||||
}
|
||||
return data;
|
||||
};
|
||||
}
|
||||
|
||||
private static String pendingEditSchema(final String productType) {
|
||||
return "spt.id as tempId, spt.product_id as savingsProductId, " + "sp.name as savingsProductName, spt.createdby_id as makerId, "
|
||||
+ "maker.username as makerUsername, spt.lastmodifiedby_id as checkerId, checker.username as checkerUsername, "
|
||||
+ "spt.created_date as createdOn, spt.lastmodified_date as checkedOn, spt.status as status, "
|
||||
+ "spt.reject_reason as rejectReason "
|
||||
+ "from m_saving_product_temp spt " + "join m_savings_product sp on sp.id = spt.product_id "
|
||||
+ "join m_appuser maker on maker.id = spt.createdby_id " + "left join m_appuser checker on checker.id = spt.lastmodifiedby_id "
|
||||
+ "where spt.product_type = '" + productType + "' ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<SavingsProductTempData> retrieveAllPendingEdits(final DepositAccountType depositAccountType) {
|
||||
final String sql = "select " + pendingEditSchema(productPendingType(depositAccountType))
|
||||
+ "and spt.status = 'PENDING' order by spt.created_date desc";
|
||||
return this.jdbcTemplate.query(sql, getPendingEditRowMapper());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavingsProductTempData retrievePendingEdit(final DepositAccountType depositAccountType, final Long productId) {
|
||||
final String sql = "select " + pendingEditSchema(productPendingType(depositAccountType))
|
||||
+ "and spt.status = 'PENDING' and spt.product_id = ?";
|
||||
try {
|
||||
return this.jdbcTemplate.queryForObject(sql, getPendingEditRowMapper(), new Object[] { productId }); // NOSONAR
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new SavingsProductNoPendingEditException(productId);
|
||||
}
|
||||
}
|
||||
|
||||
private abstract static class DepositProductMapper implements RowMapper<DepositProductData> {
|
||||
|
||||
private final String schemaSql;
|
||||
@@ -255,7 +334,9 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
|
||||
sqlBuilder.append("sp.no_penalty_nominee_heir_closure as noPenaltyNomineeHeirClosure, ");
|
||||
sqlBuilder.append("sp.deceased_settlement as deceasedSettlement, ");
|
||||
sqlBuilder.append("sp.allow_debit_txn_after_due_date as allowDebitTxnAfterDueDate, ");
|
||||
sqlBuilder.append("sp.withdrawal_fee_for_transfer as withdrawalFeeForTransfers ");
|
||||
sqlBuilder.append("sp.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, ");
|
||||
sqlBuilder.append("exists(select 1 from m_saving_product_temp spt where spt.product_id = sp.id "
|
||||
+ "and spt.product_type = 'FD' and spt.status = 'PENDING') as isPendingApproval ");
|
||||
sqlBuilder.append("from m_savings_product sp ");
|
||||
sqlBuilder.append("left join m_deposit_product_term_and_preclosure dptp on sp.id=dptp.savings_product_id ");
|
||||
sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code ");
|
||||
@@ -345,6 +426,7 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
|
||||
result.setDeceasedSettlement(rs.getBoolean("deceasedSettlement"));
|
||||
result.setAllowDebitTxnAfterDueDate(rs.getBoolean("allowDebitTxnAfterDueDate"));
|
||||
result.setWithdrawalFeeForTransfers(rs.getBoolean("withdrawalFeeForTransfers"));
|
||||
result.setIsPendingApproval(rs.getBoolean("isPendingApproval"));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -392,7 +474,9 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
|
||||
sqlBuilder.append("sp.no_penalty_nominee_heir_closure as noPenaltyNomineeHeirClosure, ");
|
||||
sqlBuilder.append("sp.deceased_settlement as deceasedSettlement, ");
|
||||
sqlBuilder.append("sp.allow_debit_txn_after_due_date as allowDebitTxnAfterDueDate, ");
|
||||
sqlBuilder.append("sp.withdrawal_fee_for_transfer as withdrawalFeeForTransfers ");
|
||||
sqlBuilder.append("sp.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, ");
|
||||
sqlBuilder.append("exists(select 1 from m_saving_product_temp spt where spt.product_id = sp.id "
|
||||
+ "and spt.product_type = 'RD' and spt.status = 'PENDING') as isPendingApproval ");
|
||||
sqlBuilder.append("from m_savings_product sp ");
|
||||
sqlBuilder.append("left join m_deposit_product_term_and_preclosure dptp on sp.id=dptp.savings_product_id ");
|
||||
sqlBuilder.append("left join m_deposit_product_recurring_detail dprd on sp.id=dprd.savings_product_id ");
|
||||
@@ -513,6 +597,7 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
|
||||
result.setDeceasedSettlement(deceasedSettlement);
|
||||
result.setAllowDebitTxnAfterDueDate(allowDebitTxnAfterDueDate);
|
||||
result.setWithdrawalFeeForTransfers(withdrawalFeeForTransfers);
|
||||
result.setIsPendingApproval(rs.getBoolean("isPendingApproval"));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
+217
-1
@@ -32,6 +32,9 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
|
||||
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;
|
||||
@@ -39,6 +42,8 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuild
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||
import org.apache.fineract.portfolio.interestratechart.service.InterestRateChartAssembler;
|
||||
@@ -48,8 +53,22 @@ import org.apache.fineract.portfolio.savings.data.DepositProductDataValidator;
|
||||
import org.apache.fineract.portfolio.savings.domain.DepositProductAssembler;
|
||||
import org.apache.fineract.portfolio.savings.domain.FixedDepositProduct;
|
||||
import org.apache.fineract.portfolio.savings.domain.FixedDepositProductRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEdit;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedField;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditCharge;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMapping;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFieldMapper;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.apache.fineract.portfolio.savings.exception.FixedDepositProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductEditPendingApprovalException;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductNoPendingEditException;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductSelfApprovalNotAllowedException;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -57,12 +76,19 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@RequiredArgsConstructor
|
||||
public class FixedDepositProductWritePlatformServiceJpaRepositoryImpl implements FixedDepositProductWritePlatformService {
|
||||
|
||||
private static final com.google.gson.Gson GSON = new com.google.gson.Gson();
|
||||
private final PlatformSecurityContext context;
|
||||
private final FixedDepositProductRepository fixedDepositProductRepository;
|
||||
private final DepositProductDataValidator fromApiJsonDataValidator;
|
||||
private final DepositProductAssembler depositProductAssembler;
|
||||
private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService;
|
||||
private final InterestRateChartAssembler chartAssembler;
|
||||
private final ProductPendingEditRepository productPendingEditRepository;
|
||||
private final ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository;
|
||||
private final ProductPendingEditChargeRepository pendingEditChargeRepository;
|
||||
private final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final CommandSourceRepository commandSourceRepository;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
@@ -94,10 +120,16 @@ public class FixedDepositProductWritePlatformServiceJpaRepositoryImpl implements
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult update(final Long productId, final JsonCommand command) {
|
||||
return updateInternal(productId, command, true);
|
||||
}
|
||||
|
||||
private CommandProcessingResult updateInternal(final Long productId, final JsonCommand command, final boolean validate) {
|
||||
|
||||
try {
|
||||
this.context.authenticatedUser();
|
||||
this.fromApiJsonDataValidator.validateForFixedDepositUpdate(command.json());
|
||||
if (validate) {
|
||||
this.fromApiJsonDataValidator.validateForFixedDepositUpdate(command.json());
|
||||
}
|
||||
|
||||
final FixedDepositProduct product = this.fixedDepositProductRepository.findById(productId)
|
||||
.orElseThrow(() -> new FixedDepositProductNotFoundException(productId));
|
||||
@@ -150,6 +182,190 @@ public class FixedDepositProductWritePlatformServiceJpaRepositoryImpl implements
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult submitEditForApproval(final Long productId, final JsonCommand command) {
|
||||
|
||||
final AppUser maker = this.context.authenticatedUser();
|
||||
final FixedDepositProduct product = this.fixedDepositProductRepository.findById(productId)
|
||||
.orElseThrow(() -> new FixedDepositProductNotFoundException(productId));
|
||||
|
||||
if (this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_FD, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.isPresent()) {
|
||||
throw new SavingsProductEditPendingApprovalException(productId);
|
||||
}
|
||||
|
||||
this.fromApiJsonDataValidator.validateForFixedDepositUpdate(command.json());
|
||||
|
||||
final ProductPendingEdit pendingEdit = new ProductPendingEdit(ProductPendingEdit.TYPE_FD, productId, maker);
|
||||
|
||||
final java.util.Set<String> submitted = ProductPendingEditFieldMapper.populateCoreFields(command, pendingEdit.getCoreFields());
|
||||
final java.util.Set<String> fdSubmitted = ProductPendingEditFieldMapper.populateFixedDepositFields(command, pendingEdit.getFixedDepositFields());
|
||||
final boolean glMappingSubmitted = ProductPendingEditFieldMapper.populateGlAccountMapping(command, pendingEdit.getGlAccountMappingFields());
|
||||
|
||||
if (!fdSubmitted.isEmpty()) {
|
||||
submitted.addAll(fdSubmitted);
|
||||
}
|
||||
if (glMappingSubmitted) {
|
||||
submitted.add("glAccountMapping");
|
||||
}
|
||||
|
||||
final CommandSource auditEntry = CommandSource.builder() //
|
||||
.actionName("UPDATE") //
|
||||
.entityName("FIXEDDEPOSITPRODUCT") //
|
||||
.resourceGetUrl("/fixeddepositproducts/" + productId) //
|
||||
.resourceId(productId) //
|
||||
.commandAsJson(command.json()) //
|
||||
.maker(maker) //
|
||||
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||
.idempotencyKey(java.util.UUID.randomUUID().toString()) //
|
||||
.sanitized(false) //
|
||||
.build();
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
for (final String fieldName : submitted) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, fieldName));
|
||||
}
|
||||
|
||||
final List<Long> chargeIds = ProductPendingEditFieldMapper.extractChargeIds(command);
|
||||
if (chargeIds != null) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "charges"));
|
||||
for (final Long chargeId : chargeIds) {
|
||||
this.pendingEditChargeRepository.save(new ProductPendingEditCharge(pendingEdit, chargeId));
|
||||
}
|
||||
}
|
||||
|
||||
final List<ProductPendingEditGlMapping> glMappings = ProductPendingEditFieldMapper.extractGlArrayMappings(command, pendingEdit);
|
||||
if (glMappings != null) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "glArrayMappings"));
|
||||
this.pendingEditGlMappingRepository.saveAll(glMappings);
|
||||
}
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(product.getId()) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult approveEdit(final Long productId) {
|
||||
|
||||
final AppUser checker = this.context.authenticatedUser();
|
||||
final ProductPendingEdit pendingEdit = this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_FD, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.orElseThrow(() -> new SavingsProductNoPendingEditException(productId));
|
||||
|
||||
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||
throw new SavingsProductSelfApprovalNotAllowedException(productId);
|
||||
}
|
||||
|
||||
final Set<String> submittedFields = new java.util.HashSet<>(
|
||||
this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId()));
|
||||
final JsonObject root = new JsonObject();
|
||||
root.addProperty("locale", "en");
|
||||
root.addProperty("dateFormat", "yyyy-MM-dd");
|
||||
ProductPendingEditFieldMapper.writeCoreFieldsToJson(root, pendingEdit.getCoreFields(), submittedFields);
|
||||
ProductPendingEditFieldMapper.writeFixedDepositFieldsToJson(root, pendingEdit.getFixedDepositFields(), submittedFields);
|
||||
if (submittedFields.contains("glAccountMapping")) {
|
||||
ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields());
|
||||
}
|
||||
if (submittedFields.contains("charges")) {
|
||||
ProductPendingEditFieldMapper.writeChargesToJson(root,
|
||||
this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
if (submittedFields.contains("glArrayMappings")) {
|
||||
ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root,
|
||||
this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
final JsonCommand replayedCommand = JsonCommand.fromJsonElement(productId, root, this.fromApiJsonHelper);
|
||||
final CommandProcessingResult result = updateInternal(productId, replayedCommand, false);
|
||||
|
||||
pendingEdit.markApproved(checker);
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
final CommandSource auditEntry = findOrCreateAuditEntry(pendingEdit, productId);
|
||||
auditEntry.markAsChecked(checker);
|
||||
auditEntry.updateForAudit(result);
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private CommandSource findOrCreateAuditEntry(final ProductPendingEdit pendingEdit, final Long productId) {
|
||||
if (pendingEdit.getCommandSourceId() != null) {
|
||||
final java.util.Optional<CommandSource> existing = this.commandSourceRepository.findById(pendingEdit.getCommandSourceId());
|
||||
if (existing.isPresent()) {
|
||||
return existing.get();
|
||||
}
|
||||
}
|
||||
final Set<String> submittedFields = new java.util.HashSet<>(
|
||||
this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId()));
|
||||
final JsonObject root = new JsonObject();
|
||||
root.addProperty("locale", "en");
|
||||
root.addProperty("dateFormat", "yyyy-MM-dd");
|
||||
ProductPendingEditFieldMapper.writeCoreFieldsToJson(root, pendingEdit.getCoreFields(), submittedFields);
|
||||
ProductPendingEditFieldMapper.writeFixedDepositFieldsToJson(root, pendingEdit.getFixedDepositFields(), submittedFields);
|
||||
if (submittedFields.contains("glAccountMapping")) {
|
||||
ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields());
|
||||
}
|
||||
if (submittedFields.contains("charges")) {
|
||||
ProductPendingEditFieldMapper.writeChargesToJson(root,
|
||||
this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
if (submittedFields.contains("glArrayMappings")) {
|
||||
ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root,
|
||||
this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
final String commandJson = GSON.toJson(root);
|
||||
|
||||
final CommandSource auditEntry = CommandSource.builder() //
|
||||
.actionName("UPDATE") //
|
||||
.entityName("FIXEDDEPOSITPRODUCT") //
|
||||
.resourceGetUrl("/fixeddepositproducts/" + productId) //
|
||||
.resourceId(productId) //
|
||||
.commandAsJson(commandJson) //
|
||||
.maker(pendingEdit.getMaker()) //
|
||||
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||
.idempotencyKey(java.util.UUID.randomUUID().toString()) //
|
||||
.sanitized(false) //
|
||||
.build();
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
return auditEntry;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult rejectEdit(final Long productId, final String rejectReason) {
|
||||
|
||||
final AppUser checker = this.context.authenticatedUser();
|
||||
final ProductPendingEdit pendingEdit = this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_FD, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.orElseThrow(() -> new SavingsProductNoPendingEditException(productId));
|
||||
|
||||
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||
throw new SavingsProductSelfApprovalNotAllowedException(productId);
|
||||
}
|
||||
|
||||
pendingEdit.markRejected(checker, rejectReason);
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
final CommandSource auditEntry = findOrCreateAuditEntry(pendingEdit, productId);
|
||||
auditEntry.markAsRejected(checker);
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(productId) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult delete(final Long productId) {
|
||||
|
||||
+224
-1
@@ -32,6 +32,9 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
|
||||
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;
|
||||
@@ -39,6 +42,8 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuild
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||
import org.apache.fineract.portfolio.interestratechart.service.InterestRateChartAssembler;
|
||||
@@ -46,10 +51,24 @@ import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.data.DepositProductDataValidator;
|
||||
import org.apache.fineract.portfolio.savings.domain.DepositProductAssembler;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEdit;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditCharge;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFieldMapper;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMapping;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedField;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.RecurringDepositProduct;
|
||||
import org.apache.fineract.portfolio.savings.domain.RecurringDepositProductRepository;
|
||||
import org.apache.fineract.portfolio.savings.exception.RecurringDepositProductNotFoundException;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductEditPendingApprovalException;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductNoPendingEditException;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductSelfApprovalNotAllowedException;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -57,12 +76,19 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@RequiredArgsConstructor
|
||||
public class RecurringDepositProductWritePlatformServiceJpaRepositoryImpl implements RecurringDepositProductWritePlatformService {
|
||||
|
||||
private static final com.google.gson.Gson GSON = new com.google.gson.Gson();
|
||||
private final PlatformSecurityContext context;
|
||||
private final RecurringDepositProductRepository recurringDepositProductRepository;
|
||||
private final DepositProductDataValidator fromApiJsonDataValidator;
|
||||
private final DepositProductAssembler depositProductAssembler;
|
||||
private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService;
|
||||
private final InterestRateChartAssembler chartAssembler;
|
||||
private final ProductPendingEditRepository productPendingEditRepository;
|
||||
private final ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository;
|
||||
private final ProductPendingEditChargeRepository pendingEditChargeRepository;
|
||||
private final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final CommandSourceRepository commandSourceRepository;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
@@ -94,10 +120,16 @@ public class RecurringDepositProductWritePlatformServiceJpaRepositoryImpl implem
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult update(final Long productId, final JsonCommand command) {
|
||||
return updateInternal(productId, command, true);
|
||||
}
|
||||
|
||||
private CommandProcessingResult updateInternal(final Long productId, final JsonCommand command, final boolean validate) {
|
||||
|
||||
try {
|
||||
this.context.authenticatedUser();
|
||||
this.fromApiJsonDataValidator.validateForRecurringDepositUpdate(command.json());
|
||||
if (validate) {
|
||||
this.fromApiJsonDataValidator.validateForRecurringDepositUpdate(command.json());
|
||||
}
|
||||
|
||||
final RecurringDepositProduct product = this.recurringDepositProductRepository.findById(productId)
|
||||
.orElseThrow(() -> new RecurringDepositProductNotFoundException(productId));
|
||||
@@ -150,6 +182,197 @@ public class RecurringDepositProductWritePlatformServiceJpaRepositoryImpl implem
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult submitEditForApproval(final Long productId, final JsonCommand command) {
|
||||
|
||||
final AppUser maker = this.context.authenticatedUser();
|
||||
final RecurringDepositProduct product = this.recurringDepositProductRepository.findById(productId)
|
||||
.orElseThrow(() -> new RecurringDepositProductNotFoundException(productId));
|
||||
|
||||
if (this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_RD, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.isPresent()) {
|
||||
throw new SavingsProductEditPendingApprovalException(productId);
|
||||
}
|
||||
|
||||
this.fromApiJsonDataValidator.validateForRecurringDepositUpdate(command.json());
|
||||
|
||||
final ProductPendingEdit pendingEdit = new ProductPendingEdit(ProductPendingEdit.TYPE_RD, productId, maker);
|
||||
|
||||
final java.util.Set<String> submitted = ProductPendingEditFieldMapper.populateCoreFields(command, pendingEdit.getCoreFields());
|
||||
final java.util.Set<String> fdSubmitted = ProductPendingEditFieldMapper.populateFixedDepositFields(command, pendingEdit.getFixedDepositFields());
|
||||
final java.util.Set<String> rdSubmitted = ProductPendingEditFieldMapper.populateRecurringDepositFields(command, pendingEdit.getRecurringDepositFields());
|
||||
final boolean glMappingSubmitted = ProductPendingEditFieldMapper.populateGlAccountMapping(command, pendingEdit.getGlAccountMappingFields());
|
||||
|
||||
if (!fdSubmitted.isEmpty()) {
|
||||
submitted.addAll(fdSubmitted);
|
||||
}
|
||||
if (!rdSubmitted.isEmpty()) {
|
||||
submitted.addAll(rdSubmitted);
|
||||
}
|
||||
if (glMappingSubmitted) {
|
||||
submitted.add("glAccountMapping");
|
||||
}
|
||||
|
||||
final CommandSource auditEntry = CommandSource.builder() //
|
||||
.actionName("UPDATE") //
|
||||
.entityName("RECURRINGDEPOSITPRODUCT") //
|
||||
.resourceGetUrl("/recurringdepositproducts/" + productId) //
|
||||
.resourceId(productId) //
|
||||
.commandAsJson(command.json()) //
|
||||
.maker(maker) //
|
||||
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||
.idempotencyKey(java.util.UUID.randomUUID().toString()) //
|
||||
.sanitized(false) //
|
||||
.build();
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
for (final String fieldName : submitted) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, fieldName));
|
||||
}
|
||||
|
||||
final List<Long> chargeIds = ProductPendingEditFieldMapper.extractChargeIds(command);
|
||||
if (chargeIds != null) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "charges"));
|
||||
for (final Long chargeId : chargeIds) {
|
||||
this.pendingEditChargeRepository.save(new ProductPendingEditCharge(pendingEdit, chargeId));
|
||||
}
|
||||
}
|
||||
|
||||
final List<ProductPendingEditGlMapping> glMappings = ProductPendingEditFieldMapper.extractGlArrayMappings(command, pendingEdit);
|
||||
if (glMappings != null) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "glArrayMappings"));
|
||||
this.pendingEditGlMappingRepository.saveAll(glMappings);
|
||||
}
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(product.getId()) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult approveEdit(final Long productId) {
|
||||
|
||||
final AppUser checker = this.context.authenticatedUser();
|
||||
final ProductPendingEdit pendingEdit = this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_RD, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.orElseThrow(() -> new SavingsProductNoPendingEditException(productId));
|
||||
|
||||
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||
throw new SavingsProductSelfApprovalNotAllowedException(productId);
|
||||
}
|
||||
|
||||
final Set<String> submittedFields = new java.util.HashSet<>(
|
||||
this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId()));
|
||||
submittedFields.remove("fdType");
|
||||
final JsonObject root = new JsonObject();
|
||||
root.addProperty("locale", "en");
|
||||
root.addProperty("dateFormat", "yyyy-MM-dd");
|
||||
ProductPendingEditFieldMapper.writeCoreFieldsToJson(root, pendingEdit.getCoreFields(), submittedFields);
|
||||
ProductPendingEditFieldMapper.writeFixedDepositFieldsToJson(root, pendingEdit.getFixedDepositFields(), submittedFields);
|
||||
ProductPendingEditFieldMapper.writeRecurringDepositFieldsToJson(root, pendingEdit.getRecurringDepositFields(), submittedFields);
|
||||
if (submittedFields.contains("glAccountMapping")) {
|
||||
ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields());
|
||||
}
|
||||
if (submittedFields.contains("charges")) {
|
||||
ProductPendingEditFieldMapper.writeChargesToJson(root,
|
||||
this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
if (submittedFields.contains("glArrayMappings")) {
|
||||
ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root,
|
||||
this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
final JsonCommand replayedCommand = JsonCommand.fromJsonElement(productId, root, this.fromApiJsonHelper);
|
||||
final CommandProcessingResult result = updateInternal(productId, replayedCommand, false);
|
||||
|
||||
pendingEdit.markApproved(checker);
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
final CommandSource auditEntry = findOrCreateAuditEntry(pendingEdit, productId);
|
||||
auditEntry.markAsChecked(checker);
|
||||
auditEntry.updateForAudit(result);
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private CommandSource findOrCreateAuditEntry(final ProductPendingEdit pendingEdit, final Long productId) {
|
||||
if (pendingEdit.getCommandSourceId() != null) {
|
||||
final java.util.Optional<CommandSource> existing = this.commandSourceRepository.findById(pendingEdit.getCommandSourceId());
|
||||
if (existing.isPresent()) {
|
||||
return existing.get();
|
||||
}
|
||||
}
|
||||
final Set<String> submittedFields = new java.util.HashSet<>(
|
||||
this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId()));
|
||||
final JsonObject root = new JsonObject();
|
||||
root.addProperty("locale", "en");
|
||||
root.addProperty("dateFormat", "yyyy-MM-dd");
|
||||
ProductPendingEditFieldMapper.writeCoreFieldsToJson(root, pendingEdit.getCoreFields(), submittedFields);
|
||||
ProductPendingEditFieldMapper.writeFixedDepositFieldsToJson(root, pendingEdit.getFixedDepositFields(), submittedFields);
|
||||
ProductPendingEditFieldMapper.writeRecurringDepositFieldsToJson(root, pendingEdit.getRecurringDepositFields(), submittedFields);
|
||||
if (submittedFields.contains("glAccountMapping")) {
|
||||
ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields());
|
||||
}
|
||||
if (submittedFields.contains("charges")) {
|
||||
ProductPendingEditFieldMapper.writeChargesToJson(root,
|
||||
this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
if (submittedFields.contains("glArrayMappings")) {
|
||||
ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root,
|
||||
this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
final String commandJson = GSON.toJson(root);
|
||||
|
||||
final CommandSource auditEntry = CommandSource.builder() //
|
||||
.actionName("UPDATE") //
|
||||
.entityName("RECURRINGDEPOSITPRODUCT") //
|
||||
.resourceGetUrl("/recurringdepositproducts/" + productId) //
|
||||
.resourceId(productId) //
|
||||
.commandAsJson(commandJson) //
|
||||
.maker(pendingEdit.getMaker()) //
|
||||
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||
.idempotencyKey(java.util.UUID.randomUUID().toString()) //
|
||||
.sanitized(false) //
|
||||
.build();
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
return auditEntry;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult rejectEdit(final Long productId, final String rejectReason) {
|
||||
|
||||
final AppUser checker = this.context.authenticatedUser();
|
||||
final ProductPendingEdit pendingEdit = this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_RD, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.orElseThrow(() -> new SavingsProductNoPendingEditException(productId));
|
||||
|
||||
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||
throw new SavingsProductSelfApprovalNotAllowedException(productId);
|
||||
}
|
||||
|
||||
pendingEdit.markRejected(checker, rejectReason);
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
final CommandSource auditEntry = findOrCreateAuditEntry(pendingEdit, productId);
|
||||
auditEntry.markAsRejected(checker);
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(productId) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult delete(final Long productId) {
|
||||
|
||||
+19
-5
@@ -172,10 +172,17 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
if (searchParameters != null) {
|
||||
|
||||
if (StringUtils.isNotBlank(searchParameters.getStatus())) {
|
||||
final Integer statusEnum = resolveStatusEnum(searchParameters.getStatus());
|
||||
if (statusEnum != null) {
|
||||
sqlBuilder.append(" and sa.status_enum = ?");
|
||||
objectArray[arrayPos++] = statusEnum;
|
||||
if ("terminated".equalsIgnoreCase(searchParameters.getStatus())) {
|
||||
sqlBuilder.append(" and sa.terminate_true = true");
|
||||
} else {
|
||||
final Integer statusEnum = resolveStatusEnum(searchParameters.getStatus());
|
||||
if (statusEnum != null) {
|
||||
sqlBuilder.append(" and sa.status_enum = ?");
|
||||
objectArray[arrayPos++] = statusEnum;
|
||||
if (statusEnum == 100) {
|
||||
sqlBuilder.append(" and (sa.terminate_true = false or sa.terminate_true is null)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,6 +255,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
case "withdrawn" -> 400;
|
||||
case "rejected" -> 500;
|
||||
case "closed" -> 600;
|
||||
case "terminated" -> 900;
|
||||
default -> {
|
||||
try {
|
||||
yield Integer.parseInt(status);
|
||||
@@ -863,6 +871,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
sqlBuilder.append("sp.msme as msme, ");
|
||||
sqlBuilder.append("COALESCE(sa.nomination_facility, sp.nomination_facility) as nominationFacility, ");
|
||||
sqlBuilder.append("COALESCE(sa.auto_debit_facility, sp.auto_debit_facility) as autoDebitFacility, ");
|
||||
sqlBuilder.append("COALESCE(sa.introducer_required, sp.introducer_required) as introducerRequired, ");
|
||||
sqlBuilder.append("COALESCE(sa.share_linking_enabled, sp.share_linking_enabled) as shareLinkingEnabled, ");
|
||||
// Facility config fields
|
||||
sqlBuilder.append("safc.atm_facility as atmFacility, ");
|
||||
sqlBuilder.append("safc.atm_withdrawal_facility as atmWithdrawalFacility, ");
|
||||
@@ -879,7 +889,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
sqlBuilder.append("safc.inter_bank_total_cap_facility as interBankTotalCapFacility, ");
|
||||
sqlBuilder.append("safc.withdrawal_facility as withdrawalFacility, ");
|
||||
sqlBuilder.append("safc.deposit_facility_kyc as depositFacilityKyc, ");
|
||||
sqlBuilder.append("safc.virtual_debit_card as virtualDebitCard ");
|
||||
sqlBuilder.append("safc.virtual_debit_card as virtualDebitCard, ");
|
||||
sqlBuilder.append("sa.terminate_true as terminateTrue ");
|
||||
sqlBuilder.append("from m_savings_account sa ");
|
||||
sqlBuilder.append("join m_savings_product sp ON sa.product_id = sp.id ");
|
||||
sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code ");
|
||||
@@ -1118,6 +1129,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
accountData.setMsme(rs.getObject("msme") != null ? rs.getBoolean("msme") : null);
|
||||
accountData.setNominationFacility(rs.getObject("nominationFacility") != null ? rs.getBoolean("nominationFacility") : null);
|
||||
accountData.setAutoDebitFacility(rs.getObject("autoDebitFacility") != null ? rs.getBoolean("autoDebitFacility") : null);
|
||||
accountData.setIntroducerRequired(rs.getObject("introducerRequired") != null ? rs.getBoolean("introducerRequired") : null);
|
||||
accountData.setShareLinkingEnabled(rs.getObject("shareLinkingEnabled") != null ? rs.getBoolean("shareLinkingEnabled") : null);
|
||||
accountData.setTerminateTrue(rs.getObject("terminateTrue") != null ? rs.getBoolean("terminateTrue") : null);
|
||||
|
||||
// Nominee as parsed map
|
||||
final String nomineeDataStr = rs.getString("nomineeData");
|
||||
|
||||
+13
@@ -1770,6 +1770,19 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
.withClientId(account.clientId()).withGroupId(account.groupId()).withSavingsId(savingsId).with(changes).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult terminate(final Long savingsId, final JsonCommand command) {
|
||||
this.context.authenticatedUser();
|
||||
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId, false);
|
||||
final Map<String, Object> changes = account.terminate();
|
||||
if (!changes.isEmpty()) {
|
||||
this.savingAccountRepositoryWrapper.save(account);
|
||||
}
|
||||
return new CommandProcessingResultBuilder().withEntityId(savingsId).withOfficeId(account.officeId())
|
||||
.withClientId(account.clientId()).withGroupId(account.groupId()).withSavingsId(savingsId).with(changes).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult unblockAccount(final Long savingsId) {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
+74
-1
@@ -38,17 +38,26 @@ import org.apache.fineract.infrastructure.security.service.PlatformSecurityConte
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfig;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimit;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKyc;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfigRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimitRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKycRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEdit;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFieldMapper;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductNoPendingEditException;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import java.util.Set;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class SavingsProductReadPlatformServiceImpl implements SavingsProductReadPlatformService {
|
||||
@@ -61,6 +70,10 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
private final SavingsProductFacilityConfigRepository facilityConfigRepository;
|
||||
private final SavingsProductFacilityLimitRepository facilityLimitRepository;
|
||||
private final SavingsProductDepositLimitKycRepository depositLimitKycRepository;
|
||||
private final ProductPendingEditRepository productPendingEditRepository;
|
||||
private final ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository;
|
||||
private final ProductPendingEditChargeRepository pendingEditChargeRepository;
|
||||
private final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository;
|
||||
private static final Gson GSON = new Gson();
|
||||
private static final Type OTHER_GL_MAPPINGS_TYPE = new TypeToken<List<Map<String, Object>>>() {}.getType();
|
||||
private static final Type OTHER_GL_MAPPINGS_MAP_TYPE = new TypeToken<Map<String, List<Map<String, Object>>>>() {}.getType();
|
||||
@@ -170,6 +183,63 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
}
|
||||
}
|
||||
|
||||
private static final String PENDING_EDIT_SCHEMA = "spt.id as tempId, spt.product_id as savingsProductId, "
|
||||
+ "sp.name as savingsProductName, spt.createdby_id as makerId, maker.username as makerUsername, "
|
||||
+ "spt.lastmodifiedby_id as checkerId, checker.username as checkerUsername, spt.created_date as createdOn, "
|
||||
+ "spt.lastmodified_date as checkedOn, spt.status as status, spt.reject_reason as rejectReason "
|
||||
+ "from m_saving_product_temp spt "
|
||||
+ "join m_savings_product sp on sp.id = spt.product_id "
|
||||
+ "join m_appuser maker on maker.id = spt.createdby_id "
|
||||
+ "left join m_appuser checker on checker.id = spt.lastmodifiedby_id "
|
||||
+ "where spt.product_type = 'SAVINGS' ";
|
||||
|
||||
private RowMapper<SavingsProductTempData> getPendingEditRowMapper() {
|
||||
return (rs, rowNum) -> {
|
||||
final SavingsProductTempData data = new SavingsProductTempData();
|
||||
data.setId(rs.getLong("tempId"));
|
||||
data.setSavingsProductId(rs.getLong("savingsProductId"));
|
||||
data.setSavingsProductName(rs.getString("savingsProductName"));
|
||||
data.setMakerId(rs.getLong("makerId"));
|
||||
data.setMakerUsername(rs.getString("makerUsername"));
|
||||
data.setCheckerId(JdbcSupport.getLong(rs, "checkerId"));
|
||||
data.setCheckerUsername(rs.getString("checkerUsername"));
|
||||
data.setCreatedOn(rs.getTimestamp("createdOn") == null ? null : rs.getTimestamp("createdOn").toLocalDateTime());
|
||||
data.setCheckedOn(rs.getTimestamp("checkedOn") == null ? null : rs.getTimestamp("checkedOn").toLocalDateTime());
|
||||
data.setStatus(rs.getString("status"));
|
||||
data.setRejectReason(rs.getString("rejectReason"));
|
||||
|
||||
final Long tempId = data.getId();
|
||||
final java.util.Optional<ProductPendingEdit> opt = this.productPendingEditRepository.findById(tempId);
|
||||
if (opt.isPresent()) {
|
||||
final ProductPendingEdit pendingEdit = opt.get();
|
||||
final Set<String> submittedFields = new java.util.HashSet<>(
|
||||
this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId()));
|
||||
final Map<String, Object> proposedChanges = ProductPendingEditFieldMapper.reconstructProposedChangesMap(
|
||||
pendingEdit, submittedFields, this.pendingEditChargeRepository, this.pendingEditGlMappingRepository, null, GSON);
|
||||
data.setProposedChanges(proposedChanges);
|
||||
}
|
||||
return data;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<SavingsProductTempData> retrieveAllPendingEdits() {
|
||||
this.context.authenticatedUser();
|
||||
final String sql = "select " + PENDING_EDIT_SCHEMA + "and spt.status = 'PENDING' order by spt.created_date desc";
|
||||
return this.jdbcTemplate.query(sql, getPendingEditRowMapper());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavingsProductTempData retrievePendingEdit(final Long productId) {
|
||||
this.context.authenticatedUser();
|
||||
final String sql = "select " + PENDING_EDIT_SCHEMA + "and spt.status = 'PENDING' and spt.product_id = ?";
|
||||
try {
|
||||
return this.jdbcTemplate.queryForObject(sql, getPendingEditRowMapper(), new Object[] { productId }); // NOSONAR
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new SavingsProductNoPendingEditException(productId);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SavingProductMapper implements RowMapper<SavingsProductData> {
|
||||
|
||||
private final String schemaSql;
|
||||
@@ -217,7 +287,9 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
sqlBuilder.append("sp.auto_post as autoPost, sp.frequency_fix_day as frequencyFixDay, sp.last_interest_date as lastInterestDate, ");
|
||||
sqlBuilder.append("sp.enable_lockin_period as enableLockinPeriod, ");
|
||||
sqlBuilder.append("sp.balance_frequency as balanceFrequency, ");
|
||||
sqlBuilder.append("sp.account_closed_zero_balance_in_day_closing as accountClosedZeroBalanceInDayClosing ");
|
||||
sqlBuilder.append("sp.account_closed_zero_balance_in_day_closing as accountClosedZeroBalanceInDayClosing, ");
|
||||
sqlBuilder.append("exists(select 1 from m_saving_product_temp spt where spt.product_id = sp.id "
|
||||
+ "and spt.product_type = 'SAVINGS' and spt.status = 'PENDING') as isPendingApproval ");
|
||||
|
||||
sqlBuilder.append("from m_savings_product sp ");
|
||||
sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code ");
|
||||
@@ -340,6 +412,7 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
productData.setEnableLockinPeriod(rs.getBoolean("enableLockinPeriod"));
|
||||
productData.setBalanceFrequency(rs.getString("balanceFrequency"));
|
||||
productData.setAccountClosedZeroBalanceInDayClosing(rs.getBoolean("accountClosedZeroBalanceInDayClosing"));
|
||||
productData.setIsPendingApproval(rs.getBoolean("isPendingApproval"));
|
||||
|
||||
return productData;
|
||||
}
|
||||
|
||||
+247
@@ -46,6 +46,11 @@ import org.apache.fineract.infrastructure.security.service.PlatformSecurityConte
|
||||
import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||
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.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductDataValidator;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKyc;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKycRepository;
|
||||
@@ -56,8 +61,24 @@ import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimitR
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProduct;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductAssembler;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEdit;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditCharge;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFacilityLimit;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFacilityLimitRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMapping;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedField;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFieldMapper;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductEditPendingApprovalException;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductNoPendingEditException;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductSelfApprovalNotAllowedException;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -66,6 +87,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@RequiredArgsConstructor
|
||||
public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements SavingsProductWritePlatformService {
|
||||
|
||||
private static final com.google.gson.Gson GSON = new com.google.gson.Gson();
|
||||
private final PlatformSecurityContext context;
|
||||
private final SavingsProductRepository savingProductRepository;
|
||||
private final SavingsProductDataValidator fromApiJsonDataValidator;
|
||||
@@ -75,6 +97,13 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
private final SavingsProductFacilityConfigRepository facilityConfigRepository;
|
||||
private final SavingsProductFacilityLimitRepository facilityLimitRepository;
|
||||
private final SavingsProductDepositLimitKycRepository depositLimitKycRepository;
|
||||
private final ProductPendingEditRepository productPendingEditRepository;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final CommandSourceRepository commandSourceRepository;
|
||||
private final ProductPendingEditChargeRepository pendingEditChargeRepository;
|
||||
private final ProductPendingEditFacilityLimitRepository pendingEditFacilityLimitRepository;
|
||||
private final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository;
|
||||
private final ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository;
|
||||
|
||||
/*
|
||||
* Guaranteed to throw an exception no matter what the data integrity issue is.
|
||||
@@ -279,6 +308,224 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult submitEditForApproval(final Long productId, final JsonCommand command) {
|
||||
|
||||
final AppUser maker = this.context.authenticatedUser();
|
||||
final SavingsProduct product = this.savingProductRepository.findById(productId)
|
||||
.orElseThrow(() -> new SavingsProductNotFoundException(productId));
|
||||
|
||||
if (this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_SAVINGS, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.isPresent()) {
|
||||
throw new SavingsProductEditPendingApprovalException(productId);
|
||||
}
|
||||
|
||||
// Reuses the same validator the direct-update path uses, so bad data can't sit "pending" undetected.
|
||||
this.fromApiJsonDataValidator.validateForUpdate(command.json(), product);
|
||||
|
||||
final ProductPendingEdit pendingEdit = new ProductPendingEdit(ProductPendingEdit.TYPE_SAVINGS, productId, maker);
|
||||
|
||||
// Typed-column storage: extract every recognised field into the pending edit's own columns
|
||||
// (plus satellite rows for the 1:many pieces) instead of relying solely on the JSON blob
|
||||
// above. proposedPayloadJson is still populated for now (safe deprecation window) but is no
|
||||
// longer what approveEdit replays from - see approveEdit().
|
||||
final java.util.Set<String> submitted = ProductPendingEditFieldMapper.populateCoreFields(command, pendingEdit.getCoreFields());
|
||||
final boolean facilityConfigSubmitted = ProductPendingEditFieldMapper.populateFacilityConfig(command,
|
||||
pendingEdit.getFacilityConfigFields());
|
||||
final boolean depositLimitKycSubmitted = ProductPendingEditFieldMapper.populateDepositLimitKyc(command,
|
||||
pendingEdit.getDepositLimitKycFields());
|
||||
final boolean glMappingSubmitted = ProductPendingEditFieldMapper.populateGlAccountMapping(command,
|
||||
pendingEdit.getGlAccountMappingFields());
|
||||
if (facilityConfigSubmitted) {
|
||||
submitted.add("accountFacilityConfig");
|
||||
}
|
||||
if (depositLimitKycSubmitted) {
|
||||
submitted.add("depositLimitKyc");
|
||||
}
|
||||
if (glMappingSubmitted) {
|
||||
submitted.add("glAccountMapping");
|
||||
}
|
||||
|
||||
// Audit-only entry so this submission is visible in System -> Audit Trails even though it bypasses the
|
||||
// native command-processing/maker-checker pipeline (the real gate is m_savings_product_temp itself).
|
||||
final CommandSource auditEntry = CommandSource.builder() //
|
||||
.actionName("UPDATE") //
|
||||
.entityName("SAVINGSPRODUCT") //
|
||||
.resourceGetUrl("/savingsproducts/" + productId) //
|
||||
.resourceId(productId) //
|
||||
.commandAsJson(command.json()) //
|
||||
.maker(maker) //
|
||||
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||
.idempotencyKey(java.util.UUID.randomUUID().toString()) //
|
||||
.sanitized(false) //
|
||||
.build();
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
for (final String fieldName : submitted) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, fieldName));
|
||||
}
|
||||
|
||||
final List<Long> chargeIds = ProductPendingEditFieldMapper.extractChargeIds(command);
|
||||
if (chargeIds != null) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "charges"));
|
||||
for (final Long chargeId : chargeIds) {
|
||||
this.pendingEditChargeRepository.save(new ProductPendingEditCharge(pendingEdit, chargeId));
|
||||
}
|
||||
}
|
||||
|
||||
final List<ProductPendingEditFacilityLimit> facilityLimits = ProductPendingEditFieldMapper.extractFacilityLimits(command,
|
||||
pendingEdit);
|
||||
if (facilityLimits != null) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "accountFacilityLimits"));
|
||||
this.pendingEditFacilityLimitRepository.saveAll(facilityLimits);
|
||||
}
|
||||
|
||||
final List<ProductPendingEditGlMapping> glMappings = ProductPendingEditFieldMapper.extractGlArrayMappings(command, pendingEdit);
|
||||
if (glMappings != null) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "glArrayMappings"));
|
||||
this.pendingEditGlMappingRepository.saveAll(glMappings);
|
||||
}
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(product.getId()) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult approveEdit(final Long productId) {
|
||||
|
||||
final AppUser checker = this.context.authenticatedUser();
|
||||
final ProductPendingEdit pendingEdit = this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_SAVINGS, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.orElseThrow(() -> new SavingsProductNoPendingEditException(productId));
|
||||
|
||||
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||
throw new SavingsProductSelfApprovalNotAllowedException(productId);
|
||||
}
|
||||
|
||||
final Set<String> submittedFields = new java.util.HashSet<>(
|
||||
this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId()));
|
||||
final JsonObject root = new JsonObject();
|
||||
root.addProperty("locale", "en");
|
||||
root.addProperty("dateFormat", "yyyy-MM-dd");
|
||||
ProductPendingEditFieldMapper.writeCoreFieldsToJson(root, pendingEdit.getCoreFields(), submittedFields);
|
||||
ProductPendingEditFieldMapper.writeFacilityConfigToJson(root, pendingEdit.getFacilityConfigFields(),
|
||||
submittedFields.contains("accountFacilityConfig"));
|
||||
ProductPendingEditFieldMapper.writeDepositLimitKycToJson(root, pendingEdit.getDepositLimitKycFields(),
|
||||
submittedFields.contains("depositLimitKyc"));
|
||||
if (submittedFields.contains("glAccountMapping")) {
|
||||
ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields());
|
||||
}
|
||||
if (submittedFields.contains("charges")) {
|
||||
ProductPendingEditFieldMapper.writeChargesToJson(root,
|
||||
this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
if (submittedFields.contains("accountFacilityLimits")) {
|
||||
ProductPendingEditFieldMapper.writeFacilityLimitsToJson(root,
|
||||
this.pendingEditFacilityLimitRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
if (submittedFields.contains("glArrayMappings")) {
|
||||
ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root,
|
||||
this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
final JsonCommand replayedCommand = JsonCommand.fromJsonElement(productId, root, this.fromApiJsonHelper);
|
||||
final CommandProcessingResult result = update(productId, replayedCommand);
|
||||
|
||||
pendingEdit.markApproved(checker);
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
final CommandSource auditEntry = findOrCreateAuditEntry(pendingEdit, productId);
|
||||
auditEntry.markAsChecked(checker);
|
||||
auditEntry.updateForAudit(result);
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private CommandSource findOrCreateAuditEntry(final ProductPendingEdit pendingEdit, final Long productId) {
|
||||
if (pendingEdit.getCommandSourceId() != null) {
|
||||
final java.util.Optional<CommandSource> existing = this.commandSourceRepository.findById(pendingEdit.getCommandSourceId());
|
||||
if (existing.isPresent()) {
|
||||
return existing.get();
|
||||
}
|
||||
}
|
||||
final Set<String> submittedFields = new java.util.HashSet<>(
|
||||
this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId()));
|
||||
final JsonObject root = new JsonObject();
|
||||
root.addProperty("locale", "en");
|
||||
root.addProperty("dateFormat", "yyyy-MM-dd");
|
||||
ProductPendingEditFieldMapper.writeCoreFieldsToJson(root, pendingEdit.getCoreFields(), submittedFields);
|
||||
ProductPendingEditFieldMapper.writeFacilityConfigToJson(root, pendingEdit.getFacilityConfigFields(),
|
||||
submittedFields.contains("accountFacilityConfig"));
|
||||
ProductPendingEditFieldMapper.writeDepositLimitKycToJson(root, pendingEdit.getDepositLimitKycFields(),
|
||||
submittedFields.contains("depositLimitKyc"));
|
||||
if (submittedFields.contains("glAccountMapping")) {
|
||||
ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields());
|
||||
}
|
||||
if (submittedFields.contains("charges")) {
|
||||
ProductPendingEditFieldMapper.writeChargesToJson(root,
|
||||
this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
if (submittedFields.contains("accountFacilityLimits")) {
|
||||
ProductPendingEditFieldMapper.writeFacilityLimitsToJson(root,
|
||||
this.pendingEditFacilityLimitRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
if (submittedFields.contains("glArrayMappings")) {
|
||||
ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root,
|
||||
this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
final String commandJson = GSON.toJson(root);
|
||||
|
||||
final CommandSource auditEntry = CommandSource.builder() //
|
||||
.actionName("UPDATE") //
|
||||
.entityName("SAVINGSPRODUCT") //
|
||||
.resourceGetUrl("/savingsproducts/" + productId) //
|
||||
.resourceId(productId) //
|
||||
.commandAsJson(commandJson) //
|
||||
.maker(pendingEdit.getMaker()) //
|
||||
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||
.idempotencyKey(java.util.UUID.randomUUID().toString()) //
|
||||
.sanitized(false) //
|
||||
.build();
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
return auditEntry;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult rejectEdit(final Long productId, final String rejectReason) {
|
||||
|
||||
final AppUser checker = this.context.authenticatedUser();
|
||||
final ProductPendingEdit pendingEdit = this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_SAVINGS, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.orElseThrow(() -> new SavingsProductNoPendingEditException(productId));
|
||||
|
||||
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||
throw new SavingsProductSelfApprovalNotAllowedException(productId);
|
||||
}
|
||||
|
||||
pendingEdit.markRejected(checker, rejectReason);
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
final CommandSource auditEntry = findOrCreateAuditEntry(pendingEdit, productId);
|
||||
auditEntry.markAsRejected(checker);
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(productId) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult delete(final Long productId) {
|
||||
|
||||
+55
-12
@@ -47,6 +47,7 @@ import org.apache.fineract.portfolio.account.domain.AccountAssociationsRepositor
|
||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionRepository;
|
||||
import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformService;
|
||||
import org.apache.fineract.portfolio.account.service.AccountNumberGenerator;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountNumberGenerator;
|
||||
import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService;
|
||||
import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository;
|
||||
@@ -95,6 +96,11 @@ import org.apache.fineract.portfolio.savings.domain.SavingsProductAssembler;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKycRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfig;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfigRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository;
|
||||
import org.apache.fineract.commands.domain.CommandSourceRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimit;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimitRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductRepository;
|
||||
@@ -308,8 +314,14 @@ public class SavingsConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(DepositProductReadPlatformService.class)
|
||||
public DepositProductReadPlatformService depositProductReadPlatformService(PlatformSecurityContext context, JdbcTemplate jdbcTemplate,
|
||||
InterestRateChartReadPlatformService interestRateChartReadPlatformService) {
|
||||
return new DepositProductReadPlatformServiceImpl(context, jdbcTemplate, interestRateChartReadPlatformService);
|
||||
InterestRateChartReadPlatformService interestRateChartReadPlatformService,
|
||||
ProductPendingEditRepository productPendingEditRepository,
|
||||
ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository,
|
||||
ProductPendingEditChargeRepository pendingEditChargeRepository,
|
||||
ProductPendingEditGlMappingRepository pendingEditGlMappingRepository) {
|
||||
return new DepositProductReadPlatformServiceImpl(context, jdbcTemplate, interestRateChartReadPlatformService,
|
||||
productPendingEditRepository, pendingEditSubmittedFieldRepository,
|
||||
pendingEditChargeRepository, pendingEditGlMappingRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -323,9 +335,17 @@ public class SavingsConfiguration {
|
||||
public FixedDepositProductWritePlatformService fixedDepositProductWritePlatformService(PlatformSecurityContext context,
|
||||
FixedDepositProductRepository fixedDepositProductRepository, DepositProductDataValidator fromApiJsonDataValidator,
|
||||
DepositProductAssembler depositProductAssembler,
|
||||
ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService, InterestRateChartAssembler chartAssembler) {
|
||||
ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService, InterestRateChartAssembler chartAssembler,
|
||||
ProductPendingEditRepository productPendingEditRepository,
|
||||
ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository,
|
||||
ProductPendingEditChargeRepository pendingEditChargeRepository,
|
||||
ProductPendingEditGlMappingRepository pendingEditGlMappingRepository,
|
||||
FromJsonHelper fromApiJsonHelper,
|
||||
CommandSourceRepository commandSourceRepository) {
|
||||
return new FixedDepositProductWritePlatformServiceJpaRepositoryImpl(context, fixedDepositProductRepository,
|
||||
fromApiJsonDataValidator, depositProductAssembler, accountMappingWritePlatformService, chartAssembler);
|
||||
fromApiJsonDataValidator, depositProductAssembler, accountMappingWritePlatformService, chartAssembler,
|
||||
productPendingEditRepository, pendingEditSubmittedFieldRepository, pendingEditChargeRepository,
|
||||
pendingEditGlMappingRepository, fromApiJsonHelper, commandSourceRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -347,9 +367,17 @@ public class SavingsConfiguration {
|
||||
public RecurringDepositProductWritePlatformService recurringDepositProductWritePlatformService(PlatformSecurityContext context,
|
||||
RecurringDepositProductRepository recurringDepositProductRepository, DepositProductDataValidator fromApiJsonDataValidator,
|
||||
DepositProductAssembler depositProductAssembler,
|
||||
ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService, InterestRateChartAssembler chartAssembler) {
|
||||
ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService, InterestRateChartAssembler chartAssembler,
|
||||
ProductPendingEditRepository productPendingEditRepository,
|
||||
ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository,
|
||||
ProductPendingEditChargeRepository pendingEditChargeRepository,
|
||||
ProductPendingEditGlMappingRepository pendingEditGlMappingRepository,
|
||||
FromJsonHelper fromApiJsonHelper,
|
||||
CommandSourceRepository commandSourceRepository) {
|
||||
return new RecurringDepositProductWritePlatformServiceJpaRepositoryImpl(context, recurringDepositProductRepository,
|
||||
fromApiJsonDataValidator, depositProductAssembler, accountMappingWritePlatformService, chartAssembler);
|
||||
fromApiJsonDataValidator, depositProductAssembler, accountMappingWritePlatformService, chartAssembler,
|
||||
productPendingEditRepository, pendingEditSubmittedFieldRepository, pendingEditChargeRepository,
|
||||
pendingEditGlMappingRepository, fromApiJsonHelper, commandSourceRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -404,11 +432,11 @@ public class SavingsConfiguration {
|
||||
GroupReadPlatformService groupReadPlatformService, CurrentProductReadPlatformService currentProductReadPlatformService,
|
||||
StaffReadPlatformService staffReadPlatformService, SavingsDropdownReadPlatformService dropdownReadPlatformService,
|
||||
ChargeReadPlatformService chargeReadPlatformService, EntityDatatableChecksReadService entityDatatableChecksReadService,
|
||||
ColumnValidator columnValidator, AccountNumberGenerator accountNumberGenerator,
|
||||
ColumnValidator columnValidator, CurrentAccountNumberGenerator currentAccountNumberGenerator,
|
||||
CodeValueReadPlatformService codeValueReadPlatformService) {
|
||||
return new CurrentAccountTemplateReadPlatformServiceImpl(context, jdbcTemplate, clientReadPlatformService, groupReadPlatformService,
|
||||
currentProductReadPlatformService, staffReadPlatformService, dropdownReadPlatformService, chargeReadPlatformService,
|
||||
entityDatatableChecksReadService, columnValidator, accountNumberGenerator, codeValueReadPlatformService);
|
||||
entityDatatableChecksReadService, columnValidator, currentAccountNumberGenerator, codeValueReadPlatformService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -472,9 +500,15 @@ public class SavingsConfiguration {
|
||||
@ConditionalOnMissingBean(SavingsProductReadPlatformService.class)
|
||||
public SavingsProductReadPlatformService savingsProductReadPlatformService(PlatformSecurityContext context, JdbcTemplate jdbcTemplate,
|
||||
FineractEntityAccessUtil fineractEntityAccessUtil, SavingsProductFacilityConfigRepository facilityConfigRepository,
|
||||
SavingsProductFacilityLimitRepository facilityLimitRepository, SavingsProductDepositLimitKycRepository depositLimitKycRepository) {
|
||||
SavingsProductFacilityLimitRepository facilityLimitRepository, SavingsProductDepositLimitKycRepository depositLimitKycRepository,
|
||||
ProductPendingEditRepository productPendingEditRepository,
|
||||
ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository,
|
||||
ProductPendingEditChargeRepository pendingEditChargeRepository,
|
||||
ProductPendingEditGlMappingRepository pendingEditGlMappingRepository) {
|
||||
return new SavingsProductReadPlatformServiceImpl(context, jdbcTemplate, fineractEntityAccessUtil, facilityConfigRepository,
|
||||
facilityLimitRepository, depositLimitKycRepository);
|
||||
facilityLimitRepository, depositLimitKycRepository,
|
||||
productPendingEditRepository, pendingEditSubmittedFieldRepository,
|
||||
pendingEditChargeRepository, pendingEditGlMappingRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -488,10 +522,19 @@ public class SavingsConfiguration {
|
||||
FineractEntityAccessUtil fineractEntityAccessUtil,
|
||||
SavingsProductFacilityConfigRepository facilityConfigRepository,
|
||||
SavingsProductFacilityLimitRepository facilityLimitRepository,
|
||||
SavingsProductDepositLimitKycRepository depositLimitKycRepository) {
|
||||
SavingsProductDepositLimitKycRepository depositLimitKycRepository,
|
||||
ProductPendingEditRepository productPendingEditRepository,
|
||||
FromJsonHelper fromApiJsonHelper,
|
||||
CommandSourceRepository commandSourceRepository,
|
||||
org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository pendingEditChargeRepository,
|
||||
org.apache.fineract.portfolio.savings.domain.ProductPendingEditFacilityLimitRepository pendingEditFacilityLimitRepository,
|
||||
org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository pendingEditGlMappingRepository,
|
||||
org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository) {
|
||||
return new SavingsProductWritePlatformServiceJpaRepositoryImpl(context, savingProductRepository, fromApiJsonDataValidator,
|
||||
savingsProductAssembler, accountMappingWritePlatformService, fineractEntityAccessUtil, facilityConfigRepository,
|
||||
facilityLimitRepository, depositLimitKycRepository);
|
||||
facilityLimitRepository, depositLimitKycRepository, productPendingEditRepository, fromApiJsonHelper,
|
||||
commandSourceRepository, pendingEditChargeRepository, pendingEditFacilityLimitRepository, pendingEditGlMappingRepository,
|
||||
pendingEditSubmittedFieldRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
+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.shareproducts.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
public class ShareProductEditPendingApprovalException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public ShareProductEditPendingApprovalException(final Long productId) {
|
||||
super("error.msg.shareproduct.edit.pending.approval",
|
||||
"Share product with identifier " + productId + " already has an edit pending approval", productId);
|
||||
}
|
||||
}
|
||||
+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.shareproducts.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
public class ShareProductNoPendingEditException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public ShareProductNoPendingEditException(final Long productId) {
|
||||
super("error.msg.shareproduct.no.pending.edit", "Share product with identifier " + productId + " has no edit pending approval",
|
||||
productId);
|
||||
}
|
||||
}
|
||||
+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.shareproducts.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
public class ShareProductSelfApprovalNotAllowedException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public ShareProductSelfApprovalNotAllowedException(final Long productId) {
|
||||
super("error.msg.shareproduct.self.approval.not.allowed",
|
||||
"The maker of the pending edit for share product " + productId + " cannot also approve or reject it", productId);
|
||||
}
|
||||
}
|
||||
+71
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.shareproducts.service;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Date;
|
||||
import java.sql.ResultSet;
|
||||
@@ -46,12 +48,20 @@ import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService;
|
||||
import org.apache.fineract.portfolio.products.data.ProductData;
|
||||
import org.apache.fineract.portfolio.products.exception.ProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.products.service.ShareProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||
import org.apache.fineract.portfolio.shareaccounts.service.SharesEnumerations;
|
||||
import org.apache.fineract.portfolio.shareproducts.data.ShareProductData;
|
||||
import org.apache.fineract.portfolio.shareproducts.data.ShareProductMarketPriceData;
|
||||
import org.apache.fineract.portfolio.shareproducts.exception.ShareProductNoPendingEditException;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEdit;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFieldMapper;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ShareProductReadPlatformServiceImpl implements ShareProductReadPlatformService {
|
||||
@@ -62,6 +72,10 @@ public class ShareProductReadPlatformServiceImpl implements ShareProductReadPlat
|
||||
private final ShareProductDropdownReadPlatformService shareProductDropdownReadPlatformService;
|
||||
private final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService;
|
||||
private final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService;
|
||||
private final ProductPendingEditRepository productPendingEditRepository;
|
||||
private final ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository;
|
||||
private final ProductPendingEditChargeRepository pendingEditChargeRepository;
|
||||
private final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository;
|
||||
private final PaginationHelper shareProductDataPaginationHelper;
|
||||
private final DatabaseSpecificSQLGenerator sqlGenerator;
|
||||
|
||||
@@ -152,6 +166,63 @@ public class ShareProductReadPlatformServiceImpl implements ShareProductReadPlat
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final Gson GSON = new Gson();
|
||||
|
||||
private static final String PENDING_EDIT_SCHEMA = "spt.id as tempId, spt.product_id as savingsProductId, "
|
||||
+ "sp.name as savingsProductName, spt.createdby_id as makerId, maker.username as makerUsername, "
|
||||
+ "spt.lastmodifiedby_id as checkerId, checker.username as checkerUsername, spt.created_date as createdOn, "
|
||||
+ "spt.lastmodified_date as checkedOn, spt.status as status, spt.reject_reason as rejectReason "
|
||||
+ "from m_saving_product_temp spt "
|
||||
+ "join m_share_product sp on sp.id = spt.product_id "
|
||||
+ "join m_appuser maker on maker.id = spt.createdby_id "
|
||||
+ "left join m_appuser checker on checker.id = spt.lastmodifiedby_id "
|
||||
+ "where spt.product_type = 'SHARE' ";
|
||||
|
||||
private RowMapper<SavingsProductTempData> getPendingEditRowMapper() {
|
||||
return (rs, rowNum) -> {
|
||||
final SavingsProductTempData data = new SavingsProductTempData();
|
||||
data.setId(rs.getLong("tempId"));
|
||||
data.setSavingsProductId(rs.getLong("savingsProductId"));
|
||||
data.setSavingsProductName(rs.getString("savingsProductName"));
|
||||
data.setMakerId(rs.getLong("makerId"));
|
||||
data.setMakerUsername(rs.getString("makerUsername"));
|
||||
data.setCheckerId(JdbcSupport.getLong(rs, "checkerId"));
|
||||
data.setCheckerUsername(rs.getString("checkerUsername"));
|
||||
data.setCreatedOn(rs.getTimestamp("createdOn") == null ? null : rs.getTimestamp("createdOn").toLocalDateTime());
|
||||
data.setCheckedOn(rs.getTimestamp("checkedOn") == null ? null : rs.getTimestamp("checkedOn").toLocalDateTime());
|
||||
data.setStatus(rs.getString("status"));
|
||||
data.setRejectReason(rs.getString("rejectReason"));
|
||||
|
||||
final Long tempId = data.getId();
|
||||
final java.util.Optional<ProductPendingEdit> opt = this.productPendingEditRepository.findById(tempId);
|
||||
if (opt.isPresent()) {
|
||||
final ProductPendingEdit pendingEdit = opt.get();
|
||||
final Set<String> submittedFields = new java.util.HashSet<>(
|
||||
this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId()));
|
||||
final Map<String, Object> proposedChanges = ProductPendingEditFieldMapper.reconstructProposedChangesMap(
|
||||
pendingEdit, submittedFields, this.pendingEditChargeRepository, this.pendingEditGlMappingRepository, null, GSON);
|
||||
data.setProposedChanges(proposedChanges);
|
||||
}
|
||||
return data;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<SavingsProductTempData> retrieveAllPendingEdits() {
|
||||
final String sql = "select " + PENDING_EDIT_SCHEMA + "and spt.status = 'PENDING' order by spt.created_date desc";
|
||||
return this.jdbcTemplate.query(sql, getPendingEditRowMapper());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavingsProductTempData retrievePendingEdit(final Long productId) {
|
||||
final String sql = "select " + PENDING_EDIT_SCHEMA + "and spt.status = 'PENDING' and spt.product_id = ?";
|
||||
try {
|
||||
return this.jdbcTemplate.queryForObject(sql, getPendingEditRowMapper(), new Object[] { productId }); // NOSONAR
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new ShareProductNoPendingEditException(productId);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class AllShareProductRowMapper implements RowMapper<ProductData> {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
+17
@@ -32,4 +32,21 @@ public interface ShareProductWritePlatformService {
|
||||
CommandProcessingResult approveShareProductDividend(Long PayOutDetailId);
|
||||
|
||||
CommandProcessingResult deleteShareProductDividend(Long PayOutDetailId);
|
||||
|
||||
/**
|
||||
* Submits an edit for approval instead of applying it directly. Fails if a pending edit already exists for this
|
||||
* product (that pending row is itself the lock).
|
||||
*/
|
||||
CommandProcessingResult submitEditForApproval(Long productId, JsonCommand command);
|
||||
|
||||
/**
|
||||
* Replays the stored pending edit through the normal update path and marks it approved. Fails if the approving
|
||||
* user is the same user who submitted the edit.
|
||||
*/
|
||||
CommandProcessingResult approveEdit(Long productId);
|
||||
|
||||
/**
|
||||
* Discards the pending edit. Fails if the rejecting user is the same user who submitted the edit.
|
||||
*/
|
||||
CommandProcessingResult rejectEdit(Long productId, String rejectReason);
|
||||
}
|
||||
|
||||
+201
@@ -22,33 +22,56 @@ import com.google.gson.JsonElement;
|
||||
import jakarta.persistence.PersistenceException;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
|
||||
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.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.share.ShareProductDividentsCreateBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEdit;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository;
|
||||
import org.apache.fineract.portfolio.shareproducts.constants.ShareProductApiConstants;
|
||||
import org.apache.fineract.portfolio.shareproducts.domain.ShareProduct;
|
||||
import org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividendPayOutDetails;
|
||||
import org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividentPayOutDetailsRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.shareproducts.domain.ShareProductRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.shareproducts.exception.DividentProcessingException;
|
||||
import org.apache.fineract.portfolio.shareproducts.exception.ShareProductEditPendingApprovalException;
|
||||
import org.apache.fineract.portfolio.shareproducts.exception.ShareProductNoPendingEditException;
|
||||
import org.apache.fineract.portfolio.shareproducts.exception.ShareProductSelfApprovalNotAllowedException;
|
||||
import org.apache.fineract.portfolio.shareproducts.serialization.ShareProductDataSerializer;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedField;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditCharge;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMapping;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFieldMapper;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.orm.jpa.JpaSystemException;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class ShareProductWritePlatformServiceJpaRepositoryImpl implements ShareProductWritePlatformService {
|
||||
|
||||
private static final com.google.gson.Gson GSON = new com.google.gson.Gson();
|
||||
private final ShareProductRepositoryWrapper repository;
|
||||
private final ShareProductDataSerializer serializer;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
@@ -56,6 +79,12 @@ public class ShareProductWritePlatformServiceJpaRepositoryImpl implements ShareP
|
||||
private final ShareProductDividendAssembler shareProductDividendAssembler;
|
||||
private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService;
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final PlatformSecurityContext context;
|
||||
private final ProductPendingEditRepository productPendingEditRepository;
|
||||
private final ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository;
|
||||
private final ProductPendingEditChargeRepository pendingEditChargeRepository;
|
||||
private final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository;
|
||||
private final CommandSourceRepository commandSourceRepository;
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult createShareProduct(JsonCommand jsonCommand) {
|
||||
@@ -180,6 +209,178 @@ public class ShareProductWritePlatformServiceJpaRepositoryImpl implements ShareP
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult submitEditForApproval(final Long productId, final JsonCommand command) {
|
||||
|
||||
final AppUser maker = this.context.authenticatedUser();
|
||||
final ShareProduct product = this.repository.findOneWithNotFoundDetection(productId);
|
||||
|
||||
if (this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_SHARE, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.isPresent()) {
|
||||
throw new ShareProductEditPendingApprovalException(productId);
|
||||
}
|
||||
|
||||
final ProductPendingEdit pendingEdit = new ProductPendingEdit(ProductPendingEdit.TYPE_SHARE, productId, maker);
|
||||
|
||||
final java.util.Set<String> submitted = ProductPendingEditFieldMapper.populateShareOnlyFields(command, pendingEdit.getShareOnlyFields());
|
||||
final boolean glMappingSubmitted = ProductPendingEditFieldMapper.populateGlAccountMapping(command, pendingEdit.getGlAccountMappingFields());
|
||||
if (glMappingSubmitted) {
|
||||
submitted.add("glAccountMapping");
|
||||
}
|
||||
|
||||
final CommandSource auditEntry = CommandSource.builder() //
|
||||
.actionName("UPDATE") //
|
||||
.entityName("SHAREPRODUCT") //
|
||||
.resourceGetUrl("/products/share/" + productId) //
|
||||
.resourceId(productId) //
|
||||
.commandAsJson(command.json()) //
|
||||
.maker(maker) //
|
||||
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||
.idempotencyKey(java.util.UUID.randomUUID().toString()) //
|
||||
.sanitized(false) //
|
||||
.build();
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
for (final String fieldName : submitted) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, fieldName));
|
||||
}
|
||||
|
||||
final List<Long> chargeIds = ProductPendingEditFieldMapper.extractChargeIds(command);
|
||||
if (chargeIds != null) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "charges"));
|
||||
for (final Long chargeId : chargeIds) {
|
||||
this.pendingEditChargeRepository.save(new ProductPendingEditCharge(pendingEdit, chargeId));
|
||||
}
|
||||
}
|
||||
|
||||
final List<ProductPendingEditGlMapping> glMappings = ProductPendingEditFieldMapper.extractGlArrayMappings(command, pendingEdit);
|
||||
if (glMappings != null) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "glArrayMappings"));
|
||||
this.pendingEditGlMappingRepository.saveAll(glMappings);
|
||||
}
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(product.getId()) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult approveEdit(final Long productId) {
|
||||
|
||||
final AppUser checker = this.context.authenticatedUser();
|
||||
final ProductPendingEdit pendingEdit = this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_SHARE, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.orElseThrow(() -> new ShareProductNoPendingEditException(productId));
|
||||
|
||||
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||
throw new ShareProductSelfApprovalNotAllowedException(productId);
|
||||
}
|
||||
|
||||
final Set<String> submittedFields = new java.util.HashSet<>(
|
||||
this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId()));
|
||||
final JsonObject root = new JsonObject();
|
||||
root.addProperty("locale", "en");
|
||||
ProductPendingEditFieldMapper.writeShareOnlyFieldsToJson(root, pendingEdit.getShareOnlyFields(), submittedFields);
|
||||
if (submittedFields.contains("glAccountMapping")) {
|
||||
ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields());
|
||||
}
|
||||
if (submittedFields.contains("charges")) {
|
||||
ProductPendingEditFieldMapper.writeChargesToJson(root,
|
||||
this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
if (submittedFields.contains("glArrayMappings")) {
|
||||
ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root,
|
||||
this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
final JsonCommand replayedCommand = JsonCommand.fromJsonElement(productId, root, this.fromApiJsonHelper);
|
||||
final CommandProcessingResult result = updateProduct(productId, replayedCommand);
|
||||
|
||||
pendingEdit.markApproved(checker);
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
final CommandSource auditEntry = findOrCreateAuditEntry(pendingEdit, productId);
|
||||
auditEntry.markAsChecked(checker);
|
||||
auditEntry.updateForAudit(result);
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private CommandSource findOrCreateAuditEntry(final ProductPendingEdit pendingEdit, final Long productId) {
|
||||
if (pendingEdit.getCommandSourceId() != null) {
|
||||
final java.util.Optional<CommandSource> existing = this.commandSourceRepository.findById(pendingEdit.getCommandSourceId());
|
||||
if (existing.isPresent()) {
|
||||
return existing.get();
|
||||
}
|
||||
}
|
||||
final Set<String> submittedFields = new java.util.HashSet<>(
|
||||
this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId()));
|
||||
final JsonObject root = new JsonObject();
|
||||
root.addProperty("locale", "en");
|
||||
ProductPendingEditFieldMapper.writeShareOnlyFieldsToJson(root, pendingEdit.getShareOnlyFields(), submittedFields);
|
||||
if (submittedFields.contains("glAccountMapping")) {
|
||||
ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields());
|
||||
}
|
||||
if (submittedFields.contains("charges")) {
|
||||
ProductPendingEditFieldMapper.writeChargesToJson(root,
|
||||
this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
if (submittedFields.contains("glArrayMappings")) {
|
||||
ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root,
|
||||
this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
final String commandJson = GSON.toJson(root);
|
||||
|
||||
final CommandSource auditEntry = CommandSource.builder() //
|
||||
.actionName("UPDATE") //
|
||||
.entityName("SHAREPRODUCT") //
|
||||
.resourceGetUrl("/products/share/" + productId) //
|
||||
.resourceId(productId) //
|
||||
.commandAsJson(commandJson) //
|
||||
.maker(pendingEdit.getMaker()) //
|
||||
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||
.idempotencyKey(java.util.UUID.randomUUID().toString()) //
|
||||
.sanitized(false) //
|
||||
.build();
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
return auditEntry;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult rejectEdit(final Long productId, final String rejectReason) {
|
||||
|
||||
final AppUser checker = this.context.authenticatedUser();
|
||||
final ProductPendingEdit pendingEdit = this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_SHARE, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.orElseThrow(() -> new ShareProductNoPendingEditException(productId));
|
||||
|
||||
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||
throw new ShareProductSelfApprovalNotAllowedException(productId);
|
||||
}
|
||||
|
||||
pendingEdit.markRejected(checker, rejectReason);
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
final CommandSource auditEntry = findOrCreateAuditEntry(pendingEdit, productId);
|
||||
auditEntry.markAsRejected(checker);
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(productId) //
|
||||
.build();
|
||||
}
|
||||
|
||||
private void handleDataIntegrityIssues(final Exception e) {
|
||||
log.error("Unknown data integrity issue with resource", e);
|
||||
throw ErrorHandler.getMappable(e, "error.msg.shareproduct.unknown.data.integrity.issue",
|
||||
|
||||
+22
-3
@@ -24,11 +24,17 @@ import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToG
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.PaginationHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
||||
import org.apache.fineract.commands.domain.CommandSourceRepository;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
|
||||
import org.apache.fineract.organisation.monetary.service.CurrencyReadPlatformService;
|
||||
import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService;
|
||||
import org.apache.fineract.portfolio.products.service.ShareProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository;
|
||||
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountReadPlatformService;
|
||||
import org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividentPayOutDetailsRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.shareproducts.domain.ShareProductRepositoryWrapper;
|
||||
@@ -86,9 +92,15 @@ public class ShareProductsConfiguration {
|
||||
ShareProductDropdownReadPlatformService shareProductDropdownReadPlatformService,
|
||||
AccountingDropdownReadPlatformService accountingDropdownReadPlatformService,
|
||||
ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService,
|
||||
PaginationHelper shareProductDataPaginationHelper, DatabaseSpecificSQLGenerator sqlGenerator) {
|
||||
PaginationHelper shareProductDataPaginationHelper, DatabaseSpecificSQLGenerator sqlGenerator,
|
||||
ProductPendingEditRepository productPendingEditRepository,
|
||||
ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository,
|
||||
ProductPendingEditChargeRepository pendingEditChargeRepository,
|
||||
ProductPendingEditGlMappingRepository pendingEditGlMappingRepository) {
|
||||
return new ShareProductReadPlatformServiceImpl(jdbcTemplate, currencyReadPlatformService, chargeReadPlatformService,
|
||||
shareProductDropdownReadPlatformService, accountingDropdownReadPlatformService, accountMappingReadPlatformService,
|
||||
productPendingEditRepository, pendingEditSubmittedFieldRepository,
|
||||
pendingEditChargeRepository, pendingEditGlMappingRepository,
|
||||
shareProductDataPaginationHelper, sqlGenerator);
|
||||
}
|
||||
|
||||
@@ -99,10 +111,17 @@ public class ShareProductsConfiguration {
|
||||
ShareProductDividentPayOutDetailsRepositoryWrapper shareProductDividentPayOutDetailsRepository,
|
||||
ShareProductDividendAssembler shareProductDividendAssembler,
|
||||
ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService,
|
||||
BusinessEventNotifierService businessEventNotifierService) {
|
||||
BusinessEventNotifierService businessEventNotifierService,
|
||||
PlatformSecurityContext context,
|
||||
ProductPendingEditRepository productPendingEditRepository,
|
||||
ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository,
|
||||
ProductPendingEditChargeRepository pendingEditChargeRepository,
|
||||
ProductPendingEditGlMappingRepository pendingEditGlMappingRepository,
|
||||
CommandSourceRepository commandSourceRepository) {
|
||||
return new ShareProductWritePlatformServiceJpaRepositoryImpl(repository, serializer, fromApiJsonHelper,
|
||||
shareProductDividentPayOutDetailsRepository, shareProductDividendAssembler, accountMappingWritePlatformService,
|
||||
businessEventNotifierService);
|
||||
businessEventNotifierService, context, productPendingEditRepository, pendingEditSubmittedFieldRepository,
|
||||
pendingEditChargeRepository, pendingEditGlMappingRepository, commandSourceRepository);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ fineract.security.oauth2.client.registrations.frontend-client.require-authorizat
|
||||
fineract.tenant.host=${FINERACT_DEFAULT_TENANTDB_HOSTNAME:localhost}
|
||||
fineract.tenant.port=${FINERACT_DEFAULT_TENANTDB_PORT:5432}
|
||||
fineract.tenant.username=${FINERACT_DEFAULT_TENANTDB_UID:postgres}
|
||||
fineract.tenant.password=${FINERACT_DEFAULT_TENANTDB_PWD:root}
|
||||
fineract.tenant.password=${FINERACT_DEFAULT_TENANTDB_PWD:Keshav@898966}
|
||||
fineract.tenant.parameters=${FINERACT_DEFAULT_TENANTDB_CONN_PARAMS:}
|
||||
fineract.tenant.timezone=${FINERACT_DEFAULT_TENANTDB_TIMEZONE:Asia/Kolkata}
|
||||
fineract.tenant.identifier=${FINERACT_DEFAULT_TENANTDB_IDENTIFIER:default}
|
||||
@@ -407,7 +407,7 @@ server.tomcat.mbeanregistry.enabled=${FINERACT_SERVER_TOMCAT_MBEANREGISTRY_ENABL
|
||||
spring.datasource.hikari.driverClassName=${FINERACT_HIKARI_DRIVER_SOURCE_CLASS_NAME:org.postgresql.Driver}
|
||||
spring.datasource.hikari.jdbcUrl=${FINERACT_HIKARI_JDBC_URL:jdbc:postgresql://localhost:5432/fineract_tenants}
|
||||
spring.datasource.hikari.username=${FINERACT_HIKARI_USERNAME:postgres}
|
||||
spring.datasource.hikari.password=${FINERACT_HIKARI_PASSWORD:313863}
|
||||
spring.datasource.hikari.password=${FINERACT_HIKARI_PASSWORD:Keshav@898966}
|
||||
spring.datasource.hikari.minimumIdle=${FINERACT_HIKARI_MINIMUM_IDLE:3}
|
||||
spring.datasource.hikari.maximumPoolSize=${FINERACT_HIKARI_MAXIMUM_POOL_SIZE:10}
|
||||
spring.datasource.hikari.idleTimeout=${FINERACT_HIKARI_IDLE_TIMEOUT:60000}
|
||||
|
||||
@@ -394,4 +394,7 @@
|
||||
<include file="parts/0364_catchup_current_product_tables.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0366_catchup_clerk_withdrawal_token.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0367_catchup_missing_tables.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0338_add_soft_delete_to_tellers.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0339_add_share_linking_enabled_to_current_account.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0340_add_terminate_true_to_savings_and_current_account.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?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
|
||||
-->
|
||||
<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 id="0338_add_soft_delete_to_tellers" author="fineract">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<and>
|
||||
<tableExists tableName="m_tellers"/>
|
||||
<not><columnExists tableName="m_tellers" columnName="is_deleted"/></not>
|
||||
</and>
|
||||
</preConditions>
|
||||
<addColumn tableName="m_tellers">
|
||||
<column name="is_deleted" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<?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 id="0339_add_share_linking_enabled_to_current_account" author="CBS">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<tableExists tableName="m_current_account"/>
|
||||
<not><columnExists tableName="m_current_account" columnName="is_linking_enable"/></not>
|
||||
</preConditions>
|
||||
<addColumn tableName="m_current_account">
|
||||
<column name="is_linking_enable" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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 id="0340_1" author="fineract">
|
||||
<addColumn tableName="m_savings_account">
|
||||
<column name="terminate_true" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0340_2" author="fineract">
|
||||
<addColumn tableName="m_current_account">
|
||||
<column name="terminate_true" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+2
@@ -495,6 +495,7 @@ public final class FixedDepositProductData extends DepositProductData {
|
||||
private Boolean allowDebitTxnAfterDueDate;
|
||||
private Boolean accountClosedZeroBalanceInDayClosing;
|
||||
private String balanceFrequency;
|
||||
private Boolean isPendingApproval;
|
||||
|
||||
// Template option setters
|
||||
public void setInterestCalculationTypeOption(Collection<org.apache.fineract.infrastructure.core.data.EnumOptionData> v) { this.interestCalculationTypeOption = v; }
|
||||
@@ -541,6 +542,7 @@ public final class FixedDepositProductData extends DepositProductData {
|
||||
public void setAllowDebitTxnAfterDueDate(Boolean v) { this.allowDebitTxnAfterDueDate = v; }
|
||||
public void setAccountClosedZeroBalanceInDayClosing(Boolean v) { this.accountClosedZeroBalanceInDayClosing = v; }
|
||||
public void setBalanceFrequency(String v) { this.balanceFrequency = v; }
|
||||
public void setIsPendingApproval(Boolean v) { this.isPendingApproval = v; }
|
||||
|
||||
// CBS field getters
|
||||
public String getProductCode() { return productCode; }
|
||||
|
||||
+2
@@ -98,6 +98,7 @@ public final class RecurringDepositProductData extends DepositProductData {
|
||||
private Boolean deceasedSettlement;
|
||||
private Boolean allowDebitTxnAfterDueDate;
|
||||
private Boolean withdrawalFeeForTransfers;
|
||||
private Boolean isPendingApproval;
|
||||
|
||||
public void setInterestCalculationTypeOption(Collection<EnumOptionData> v) { this.interestCalculationTypeOption = v; }
|
||||
public void setPeriodFrequencyOptions(Collection<EnumOptionData> v) { this.periodFrequencyOptions = v; }
|
||||
@@ -140,6 +141,7 @@ public final class RecurringDepositProductData extends DepositProductData {
|
||||
public void setDeceasedSettlement(Boolean v) { this.deceasedSettlement = v; }
|
||||
public void setAllowDebitTxnAfterDueDate(Boolean v) { this.allowDebitTxnAfterDueDate = v; }
|
||||
public void setWithdrawalFeeForTransfers(Boolean v) { this.withdrawalFeeForTransfers = v; }
|
||||
public void setIsPendingApproval(Boolean v) { this.isPendingApproval = v; }
|
||||
|
||||
/** Copy all CBS custom fields from source into this instance. */
|
||||
public void copyCbsFieldsFrom(final RecurringDepositProductData src) {
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ public class SavingsAccountConstant extends SavingsApiConstants {
|
||||
introducerIdParamName, introducerNameParamName, introducerCifNumberParamName,
|
||||
nomineeParamName, "nominees", linkedAccountIdParamName, linkedAccountTypeParamName, linkedAccountNoParamName,
|
||||
accountFacilityConfigParamName, jointHoldersParamName,
|
||||
balanceSwapOptionParamName, msmeParamName, autoDebitFacilityParamName));
|
||||
balanceSwapOptionParamName, msmeParamName, autoDebitFacilityParamName, introducerRequiredParamName, shareLinkingEnabledParamName));
|
||||
|
||||
/**
|
||||
* These parameters will match the class level parameters of {@link SavingsAccountData}. Where possible, we try to
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Embeddable
|
||||
@Getter
|
||||
@Setter
|
||||
public class CurrentOnlyEditFields {
|
||||
|
||||
@Column(name = "avg_balance_calc_period")
|
||||
private String avgBalanceCalcPeriod;
|
||||
|
||||
@Column(name = "valid_for_period")
|
||||
private String validForPeriod;
|
||||
|
||||
@Column(name = "od_account_closed_zero_balance_in_day_closing")
|
||||
private Boolean odAccountClosedZeroBalanceInDayClosing;
|
||||
}
|
||||
+17
@@ -33,6 +33,7 @@ import jakarta.persistence.Embedded;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.OneToOne;
|
||||
import jakarta.persistence.PostLoad;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
@@ -181,11 +182,27 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistableCustom<L
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
@PostLoad
|
||||
public void postLoad() {
|
||||
if (this.preClosureDetail == null) {
|
||||
this.preClosureDetail = new DepositPreClosureDetail();
|
||||
}
|
||||
if (this.depositTermDetail == null) {
|
||||
this.depositTermDetail = new DepositTermDetail();
|
||||
}
|
||||
}
|
||||
|
||||
public DepositPreClosureDetail depositPreClosureDetail() {
|
||||
if (this.preClosureDetail == null) {
|
||||
this.preClosureDetail = new DepositPreClosureDetail();
|
||||
}
|
||||
return this.preClosureDetail;
|
||||
}
|
||||
|
||||
public DepositTermDetail depositTermDetail() {
|
||||
if (this.depositTermDetail == null) {
|
||||
this.depositTermDetail = new DepositTermDetail();
|
||||
}
|
||||
return this.depositTermDetail;
|
||||
}
|
||||
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 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.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* Mirrors the "depositLimitKyc" JSON object - a 1:1 relationship today, so kept flat here. Note:
|
||||
* the real JSON shape is nested ({@code {general: {kycType, limit}, minor: {...}, smallSaving:
|
||||
* {...}}}), not flat siblings - the replay step is responsible for re-nesting these columns back
|
||||
* into that shape before handing off to the unchanged assembler.
|
||||
*/
|
||||
@Embeddable
|
||||
@Getter
|
||||
@Setter
|
||||
public class DepositLimitKycEditFields {
|
||||
|
||||
@Column(name = "kyc_general_type")
|
||||
private String generalType;
|
||||
|
||||
@Column(name = "kyc_general_limit")
|
||||
private String generalLimit;
|
||||
|
||||
@Column(name = "kyc_minor_type")
|
||||
private String minorType;
|
||||
|
||||
@Column(name = "kyc_minor_limit")
|
||||
private Long minorLimit;
|
||||
|
||||
@Column(name = "kyc_small_saving_type")
|
||||
private String smallSavingType;
|
||||
|
||||
@Column(name = "kyc_small_saving_limit")
|
||||
private Long smallSavingLimit;
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* 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.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/** Mirrors the "accountFacilityConfig" JSON object - a 1:1 relationship today, so kept flat here. */
|
||||
@Embeddable
|
||||
@Getter
|
||||
@Setter
|
||||
public class FacilityConfigEditFields {
|
||||
|
||||
@Column(name = "fc_atm_facility")
|
||||
private Boolean atmFacility;
|
||||
|
||||
@Column(name = "fc_atm_withdrawal_facility")
|
||||
private Boolean atmWithdrawalFacility;
|
||||
|
||||
@Column(name = "fc_upi_facility")
|
||||
private Boolean upiFacility;
|
||||
|
||||
@Column(name = "fc_pos_transactions_facility")
|
||||
private Boolean posTransactionsFacility;
|
||||
|
||||
@Column(name = "fc_cheque_book")
|
||||
private Boolean chequeBook;
|
||||
|
||||
@Column(name = "fc_imps_facility")
|
||||
private Boolean impsFacility;
|
||||
|
||||
@Column(name = "fc_sms_alerts")
|
||||
private Boolean smsAlerts;
|
||||
|
||||
@Column(name = "fc_bbps_facility")
|
||||
private Boolean bbpsFacility;
|
||||
|
||||
@Column(name = "fc_insurance_cover")
|
||||
private Boolean insuranceCover;
|
||||
|
||||
@Column(name = "fc_bepg_facility")
|
||||
private Boolean bepgFacility;
|
||||
|
||||
@Column(name = "fc_neft_facility")
|
||||
private Boolean neftFacility;
|
||||
|
||||
@Column(name = "fc_rtgs_facility")
|
||||
private Boolean rtgsFacility;
|
||||
|
||||
@Column(name = "fc_inter_bank_total_cap_facility")
|
||||
private Boolean interBankTotalCapFacility;
|
||||
|
||||
@Column(name = "fc_withdrawal_facility")
|
||||
private Boolean withdrawalFacility;
|
||||
|
||||
@Column(name = "fc_deposit_facility_kyc")
|
||||
private Boolean depositFacilityKyc;
|
||||
|
||||
@Column(name = "fc_virtual_debit_card")
|
||||
private Boolean virtualDebitCard;
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* 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.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/** Also used by Recurring Deposit, which extends Fixed Deposit. */
|
||||
@Embeddable
|
||||
@Getter
|
||||
@Setter
|
||||
public class FixedDepositEditFields {
|
||||
|
||||
@Column(name = "fd_type")
|
||||
private String fdType;
|
||||
|
||||
@Column(name = "pre_closure_penal_applicable")
|
||||
private Boolean preClosurePenalApplicable;
|
||||
|
||||
@Column(name = "pre_closure_penal_interest")
|
||||
private BigDecimal preClosurePenalInterest;
|
||||
|
||||
@Column(name = "pre_closure_penal_interest_on_type_id")
|
||||
private Integer preClosurePenalInterestOnTypeId;
|
||||
|
||||
@Column(name = "min_deposit_term")
|
||||
private Integer minDepositTerm;
|
||||
|
||||
@Column(name = "max_deposit_term")
|
||||
private Integer maxDepositTerm;
|
||||
|
||||
@Column(name = "min_deposit_term_type_id")
|
||||
private Integer minDepositTermTypeId;
|
||||
|
||||
@Column(name = "max_deposit_term_type_id")
|
||||
private Integer maxDepositTermTypeId;
|
||||
|
||||
@Column(name = "in_multiples_of_deposit_term")
|
||||
private Integer inMultiplesOfDepositTerm;
|
||||
|
||||
@Column(name = "in_multiples_of_deposit_term_type_id")
|
||||
private Integer inMultiplesOfDepositTermTypeId;
|
||||
|
||||
@Column(name = "min_deposit_amount")
|
||||
private BigDecimal minDepositAmount;
|
||||
|
||||
@Column(name = "max_deposit_amount")
|
||||
private BigDecimal maxDepositAmount;
|
||||
|
||||
@Column(name = "deposit_amount")
|
||||
private BigDecimal depositAmount;
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* 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.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* Scalar (1:1) GL account mapping ids. No FK constraints to the accounting GL table - the
|
||||
* existing validator already checks GL account validity at submit time. The 3 array-shaped GL
|
||||
* mappings (payment-channel/fee/penalty to income account) live in m_product_temp_gl_mapping
|
||||
* instead, since those are genuinely one-to-many.
|
||||
*/
|
||||
@Embeddable
|
||||
@Getter
|
||||
@Setter
|
||||
public class GlAccountMappingEditFields {
|
||||
|
||||
@Column(name = "gl_savings_reference_account_id")
|
||||
private Long savingsReferenceAccountId;
|
||||
|
||||
@Column(name = "gl_savings_control_account_id")
|
||||
private Long savingsControlAccountId;
|
||||
|
||||
@Column(name = "gl_income_from_fee_account_id")
|
||||
private Long incomeFromFeeAccountId;
|
||||
|
||||
@Column(name = "gl_income_from_penalty_account_id")
|
||||
private Long incomeFromPenaltyAccountId;
|
||||
|
||||
@Column(name = "gl_interest_on_savings_account_id")
|
||||
private Long interestOnSavingsAccountId;
|
||||
|
||||
@Column(name = "gl_transfers_in_suspense_account_id")
|
||||
private Long transfersInSuspenseAccountId;
|
||||
|
||||
@Column(name = "gl_overdraft_portfolio_control_id")
|
||||
private Long overdraftPortfolioControlId;
|
||||
|
||||
@Column(name = "gl_income_from_interest_id")
|
||||
private Long incomeFromInterestId;
|
||||
|
||||
@Column(name = "gl_write_off_account_id")
|
||||
private Long writeOffAccountId;
|
||||
|
||||
@Column(name = "gl_escheat_liability_id")
|
||||
private Long escheatLiabilityId;
|
||||
|
||||
@Column(name = "gl_penalties_receivable_account_id")
|
||||
private Long penaltiesReceivableAccountId;
|
||||
|
||||
@Column(name = "gl_interest_receivable_account_id")
|
||||
private Long interestReceivableAccountId;
|
||||
|
||||
@Column(name = "gl_fees_receivable_account_id")
|
||||
private Long feesReceivableAccountId;
|
||||
|
||||
@Column(name = "gl_interest_payable_account_id")
|
||||
private Long interestPayableAccountId;
|
||||
}
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
/**
|
||||
* 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.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embedded;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.Lob;
|
||||
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.useradministration.domain.AppUser;
|
||||
|
||||
/**
|
||||
* Holds a single pending product edit, shared across product types (Savings, Current Account, Share) in one table
|
||||
* (m_savings_product_temp). Presence of a row here for a given (productType, productId) with
|
||||
* {@code status = PENDING} is itself the lock - no separate "is locked" column anywhere. The full edit request body
|
||||
* is kept in {@link #proposedPayloadJson} so approval can replay it through each product's normal update path
|
||||
* (covering charges, GL/accounting mappings and other related tables, not just scalar columns).
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_saving_product_temp")
|
||||
@Getter
|
||||
@Setter
|
||||
public class ProductPendingEdit extends AbstractPersistableCustom<Long> {
|
||||
|
||||
public static final String STATUS_PENDING = "PENDING";
|
||||
public static final String STATUS_APPROVED = "APPROVED";
|
||||
public static final String STATUS_REJECTED = "REJECTED";
|
||||
|
||||
public static final String TYPE_SAVINGS = "SAVINGS";
|
||||
public static final String TYPE_CURRENT = "CURRENT";
|
||||
public static final String TYPE_SHARE = "SHARE";
|
||||
public static final String TYPE_FD = "FD";
|
||||
public static final String TYPE_RD = "RD";
|
||||
|
||||
@Column(name = "product_id", nullable = false)
|
||||
private Long productId;
|
||||
|
||||
@Column(name = "product_type", nullable = false, length = 20)
|
||||
private String productType;
|
||||
|
||||
@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;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* "<productType>:<productId>" while PENDING, NULL otherwise - the real DB-level lock. Unique constraints treat
|
||||
* NULL as distinct in both PostgreSQL and MySQL/MariaDB, so only one PENDING row per (productType, productId)
|
||||
* can ever exist, while resolved rows are kept indefinitely for audit history without blocking future edits.
|
||||
*/
|
||||
@Column(name = "pending_lock_key", length = 40)
|
||||
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;
|
||||
|
||||
/**
|
||||
* Typed replacement for proposedPayloadJson - one embeddable group per concern, all mapped
|
||||
* onto this same physical table. proposedPayloadJson is still populated (kept for a safe
|
||||
* deprecation window) but a product type's write-service uses these typed columns, plus the
|
||||
* satellite tables (ProductPendingEditCharge/FacilityLimit/GlMapping/MarketPrice) and the
|
||||
* ProductPendingEditSubmittedField manifest, to build and replay an edit once its cutover
|
||||
* (see SavingsProductWritePlatformServiceJpaRepositoryImpl and siblings) is complete.
|
||||
*/
|
||||
@Embedded
|
||||
private SavingsCoreEditFields coreFields = new SavingsCoreEditFields();
|
||||
|
||||
@Embedded
|
||||
private CurrentOnlyEditFields currentOnlyFields = new CurrentOnlyEditFields();
|
||||
|
||||
@Embedded
|
||||
private ShareOnlyEditFields shareOnlyFields = new ShareOnlyEditFields();
|
||||
|
||||
@Embedded
|
||||
private FixedDepositEditFields fixedDepositFields = new FixedDepositEditFields();
|
||||
|
||||
@Embedded
|
||||
private RecurringDepositEditFields recurringDepositFields = new RecurringDepositEditFields();
|
||||
|
||||
@Embedded
|
||||
private FacilityConfigEditFields facilityConfigFields = new FacilityConfigEditFields();
|
||||
|
||||
@Embedded
|
||||
private DepositLimitKycEditFields depositLimitKycFields = new DepositLimitKycEditFields();
|
||||
|
||||
@Embedded
|
||||
private GlAccountMappingEditFields glAccountMappingFields = new GlAccountMappingEditFields();
|
||||
|
||||
protected ProductPendingEdit() {}
|
||||
|
||||
public ProductPendingEdit(final String productType, final Long productId, final AppUser maker) {
|
||||
this.productType = productType;
|
||||
this.productId = productId;
|
||||
this.maker = maker;
|
||||
this.createdOn = LocalDateTime.now();
|
||||
this.status = STATUS_PENDING;
|
||||
this.pendingLockKey = lockKeyFor(productType, productId);
|
||||
}
|
||||
|
||||
public static String lockKeyFor(final String productType, final Long productId) {
|
||||
return productType + ":" + productId;
|
||||
}
|
||||
|
||||
public Long getProductId() {
|
||||
return this.productId;
|
||||
}
|
||||
|
||||
public String getProductType() {
|
||||
return this.productType;
|
||||
}
|
||||
|
||||
public AppUser getMaker() {
|
||||
return this.maker;
|
||||
}
|
||||
|
||||
public AppUser getChecker() {
|
||||
return this.checker;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedOn() {
|
||||
return this.createdOn;
|
||||
}
|
||||
|
||||
public LocalDateTime getCheckedOn() {
|
||||
return this.checkedOn;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
public String getRejectReason() {
|
||||
return this.rejectReason;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Long getCommandSourceId() {
|
||||
return this.commandSourceId;
|
||||
}
|
||||
|
||||
public void setCommandSourceId(final Long commandSourceId) {
|
||||
this.commandSourceId = commandSourceId;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* 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.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
|
||||
/** One row per charge id proposed to be attached to the product, mirroring the "charges" array. */
|
||||
@Entity
|
||||
@Table(name = "m_product_temp_charge")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class ProductPendingEditCharge extends AbstractPersistableCustom<Long> {
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "pending_edit_id", nullable = false)
|
||||
private ProductPendingEdit pendingEdit;
|
||||
|
||||
@Column(name = "charge_id", nullable = false)
|
||||
private Long chargeId;
|
||||
|
||||
public ProductPendingEditCharge(final ProductPendingEdit pendingEdit, final Long chargeId) {
|
||||
this.pendingEdit = pendingEdit;
|
||||
this.chargeId = chargeId;
|
||||
}
|
||||
}
|
||||
+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.savings.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface ProductPendingEditChargeRepository extends JpaRepository<ProductPendingEditCharge, Long> {
|
||||
|
||||
List<ProductPendingEditCharge> findByPendingEditId(Long pendingEditId);
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 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.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
|
||||
/** One row per entry of the "accountFacilityLimits" array. Values can be "UNLIMITED"/"N/A"/numeric-as-string. */
|
||||
@Entity
|
||||
@Table(name = "m_product_temp_facility_limit")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class ProductPendingEditFacilityLimit extends AbstractPersistableCustom<Long> {
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "pending_edit_id", nullable = false)
|
||||
private ProductPendingEdit pendingEdit;
|
||||
|
||||
@Column(name = "limit_type", nullable = false)
|
||||
private String limitType;
|
||||
|
||||
@Column(name = "general_limit")
|
||||
private String generalLimit;
|
||||
|
||||
@Column(name = "minor_account_limit")
|
||||
private String minorAccountLimit;
|
||||
|
||||
@Column(name = "small_saving_limit")
|
||||
private String smallSavingLimit;
|
||||
|
||||
public ProductPendingEditFacilityLimit(final ProductPendingEdit pendingEdit, final String limitType, final String generalLimit,
|
||||
final String minorAccountLimit, final String smallSavingLimit) {
|
||||
this.pendingEdit = pendingEdit;
|
||||
this.limitType = limitType;
|
||||
this.generalLimit = generalLimit;
|
||||
this.minorAccountLimit = minorAccountLimit;
|
||||
this.smallSavingLimit = smallSavingLimit;
|
||||
}
|
||||
}
|
||||
+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.savings.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface ProductPendingEditFacilityLimitRepository extends JpaRepository<ProductPendingEditFacilityLimit, Long> {
|
||||
|
||||
List<ProductPendingEditFacilityLimit> findByPendingEditId(Long pendingEditId);
|
||||
}
|
||||
+996
@@ -0,0 +1,996 @@
|
||||
/**
|
||||
* 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.savings.domain;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
|
||||
/**
|
||||
* Shared submit-time extraction / approve-time reconstruction logic for the scalar and 1:1
|
||||
* "container" fields that Savings, Current, Fixed Deposit and Recurring Deposit products all have
|
||||
* in common (FD/RD extend the Savings field set). Each populateXxx method reads present JSON
|
||||
* parameters into the corresponding typed embeddable, returning the set of parameter names it
|
||||
* found (which the caller must persist as {@link ProductPendingEditSubmittedField} rows - this is
|
||||
* how the manifest preserves "field omitted" vs "field explicitly null" vs "field has a value").
|
||||
* Each writeXxxToJson method is the exact inverse, used at approval time to rebuild a JsonObject
|
||||
* equivalent to what the maker originally submitted, before handing it to the unchanged
|
||||
* update()/updateProduct() methods via JsonCommand.fromJsonElement(...).
|
||||
*
|
||||
* Share Product's field set is largely disjoint from this shared core and is mapped separately.
|
||||
*/
|
||||
public final class ProductPendingEditFieldMapper {
|
||||
|
||||
private ProductPendingEditFieldMapper() {}
|
||||
|
||||
// ---- Savings/Current/FD/RD shared core fields ----------------------------------------------
|
||||
|
||||
public static Set<String> populateCoreFields(final JsonCommand command, final SavingsCoreEditFields f) {
|
||||
final Set<String> present = new HashSet<>();
|
||||
|
||||
if (command.hasParameter("name")) {
|
||||
f.setName(command.stringValueOfParameterNamed("name"));
|
||||
present.add("name");
|
||||
}
|
||||
if (command.hasParameter("shortName")) {
|
||||
f.setShortName(command.stringValueOfParameterNamed("shortName"));
|
||||
present.add("shortName");
|
||||
}
|
||||
if (command.hasParameter("description")) {
|
||||
f.setDescription(command.stringValueOfParameterNamed("description"));
|
||||
present.add("description");
|
||||
}
|
||||
if (command.hasParameter("currencyCode")) {
|
||||
f.setCurrencyCode(command.stringValueOfParameterNamed("currencyCode"));
|
||||
present.add("currencyCode");
|
||||
}
|
||||
if (command.hasParameter("digitsAfterDecimal")) {
|
||||
f.setDigitsAfterDecimal(command.integerValueOfParameterNamed("digitsAfterDecimal"));
|
||||
present.add("digitsAfterDecimal");
|
||||
}
|
||||
if (command.hasParameter("inMultiplesOf")) {
|
||||
f.setInMultiplesOf(command.integerValueOfParameterNamed("inMultiplesOf"));
|
||||
present.add("inMultiplesOf");
|
||||
}
|
||||
if (command.hasParameter("accountingRule")) {
|
||||
f.setAccountingRule(command.integerValueOfParameterNamed("accountingRule"));
|
||||
present.add("accountingRule");
|
||||
}
|
||||
if (command.hasParameter("nominalAnnualInterestRate")) {
|
||||
f.setNominalAnnualInterestRate(command.bigDecimalValueOfParameterNamed("nominalAnnualInterestRate"));
|
||||
present.add("nominalAnnualInterestRate");
|
||||
}
|
||||
if (command.hasParameter("interestCompoundingPeriodType")) {
|
||||
f.setInterestCompoundingPeriodType(command.integerValueOfParameterNamed("interestCompoundingPeriodType"));
|
||||
present.add("interestCompoundingPeriodType");
|
||||
}
|
||||
if (command.hasParameter("interestPostingPeriodType")) {
|
||||
f.setInterestPostingPeriodType(command.integerValueOfParameterNamed("interestPostingPeriodType"));
|
||||
present.add("interestPostingPeriodType");
|
||||
}
|
||||
if (command.hasParameter("interestCalculationType")) {
|
||||
f.setInterestCalculationType(command.integerValueOfParameterNamed("interestCalculationType"));
|
||||
present.add("interestCalculationType");
|
||||
}
|
||||
if (command.hasParameter("interestCalculationDaysInYearType")) {
|
||||
f.setInterestCalculationDaysInYearType(command.integerValueOfParameterNamed("interestCalculationDaysInYearType"));
|
||||
present.add("interestCalculationDaysInYearType");
|
||||
}
|
||||
if (command.hasParameter("minRequiredOpeningBalance")) {
|
||||
f.setMinRequiredOpeningBalance(command.bigDecimalValueOfParameterNamed("minRequiredOpeningBalance"));
|
||||
present.add("minRequiredOpeningBalance");
|
||||
}
|
||||
if (command.hasParameter("lockinPeriodFrequency")) {
|
||||
f.setLockinPeriodFrequency(command.integerValueOfParameterNamed("lockinPeriodFrequency"));
|
||||
present.add("lockinPeriodFrequency");
|
||||
}
|
||||
if (command.hasParameter("lockinPeriodFrequencyType")) {
|
||||
f.setLockinPeriodFrequencyType(command.integerValueOfParameterNamed("lockinPeriodFrequencyType"));
|
||||
present.add("lockinPeriodFrequencyType");
|
||||
}
|
||||
if (command.hasParameter("withdrawalFeeForTransfers")) {
|
||||
f.setWithdrawalFeeForTransfers(command.booleanObjectValueOfParameterNamed("withdrawalFeeForTransfers"));
|
||||
present.add("withdrawalFeeForTransfers");
|
||||
}
|
||||
if (command.hasParameter("allowOverdraft")) {
|
||||
f.setAllowOverdraft(command.booleanObjectValueOfParameterNamed("allowOverdraft"));
|
||||
present.add("allowOverdraft");
|
||||
}
|
||||
if (command.hasParameter("overdraftLimit")) {
|
||||
f.setOverdraftLimit(command.bigDecimalValueOfParameterNamed("overdraftLimit"));
|
||||
present.add("overdraftLimit");
|
||||
}
|
||||
if (command.hasParameter("nominalAnnualInterestRateOverdraft")) {
|
||||
f.setNominalAnnualInterestRateOverdraft(command.bigDecimalValueOfParameterNamed("nominalAnnualInterestRateOverdraft"));
|
||||
present.add("nominalAnnualInterestRateOverdraft");
|
||||
}
|
||||
if (command.hasParameter("minOverdraftForInterestCalculation")) {
|
||||
f.setMinOverdraftForInterestCalculation(command.bigDecimalValueOfParameterNamed("minOverdraftForInterestCalculation"));
|
||||
present.add("minOverdraftForInterestCalculation");
|
||||
}
|
||||
if (command.hasParameter("enforceMinRequiredBalance")) {
|
||||
f.setEnforceMinRequiredBalance(command.booleanObjectValueOfParameterNamed("enforceMinRequiredBalance"));
|
||||
present.add("enforceMinRequiredBalance");
|
||||
}
|
||||
if (command.hasParameter("minRequiredBalance")) {
|
||||
f.setMinRequiredBalance(command.bigDecimalValueOfParameterNamed("minRequiredBalance"));
|
||||
present.add("minRequiredBalance");
|
||||
}
|
||||
if (command.hasParameter("lienAllowed")) {
|
||||
f.setLienAllowed(command.booleanObjectValueOfParameterNamed("lienAllowed"));
|
||||
present.add("lienAllowed");
|
||||
}
|
||||
if (command.hasParameter("maxAllowedLienLimit")) {
|
||||
f.setMaxAllowedLienLimit(command.bigDecimalValueOfParameterNamed("maxAllowedLienLimit"));
|
||||
present.add("maxAllowedLienLimit");
|
||||
}
|
||||
if (command.hasParameter("minBalanceForInterestCalculation")) {
|
||||
f.setMinBalanceForInterestCalculation(command.bigDecimalValueOfParameterNamed("minBalanceForInterestCalculation"));
|
||||
present.add("minBalanceForInterestCalculation");
|
||||
}
|
||||
if (command.hasParameter("withHoldTax")) {
|
||||
f.setWithHoldTax(command.booleanObjectValueOfParameterNamed("withHoldTax"));
|
||||
present.add("withHoldTax");
|
||||
}
|
||||
if (command.hasParameter("taxGroupId")) {
|
||||
f.setTaxGroupId(command.longValueOfParameterNamed("taxGroupId"));
|
||||
present.add("taxGroupId");
|
||||
}
|
||||
if (command.hasParameter("otherGlMappings")) {
|
||||
f.setOtherGlMappings(command.stringValueOfParameterNamed("otherGlMappings"));
|
||||
present.add("otherGlMappings");
|
||||
}
|
||||
if (command.hasParameter("isDormancyTrackingActive")) {
|
||||
f.setIsDormancyTrackingActive(command.booleanObjectValueOfParameterNamed("isDormancyTrackingActive"));
|
||||
present.add("isDormancyTrackingActive");
|
||||
}
|
||||
if (command.hasParameter("daysToInactive")) {
|
||||
f.setDaysToInactive(command.integerValueOfParameterNamed("daysToInactive"));
|
||||
present.add("daysToInactive");
|
||||
}
|
||||
if (command.hasParameter("daysToDormancy")) {
|
||||
f.setDaysToDormancy(command.integerValueOfParameterNamed("daysToDormancy"));
|
||||
present.add("daysToDormancy");
|
||||
}
|
||||
if (command.hasParameter("daysToEscheat")) {
|
||||
f.setDaysToEscheat(command.integerValueOfParameterNamed("daysToEscheat"));
|
||||
present.add("daysToEscheat");
|
||||
}
|
||||
if (command.hasParameter("accountType")) {
|
||||
f.setAccountType(command.stringValueOfParameterNamed("accountType"));
|
||||
present.add("accountType");
|
||||
}
|
||||
if (command.hasParameter("customerSegment")) {
|
||||
f.setCustomerSegment(command.stringValueOfParameterNamed("customerSegment"));
|
||||
present.add("customerSegment");
|
||||
}
|
||||
if (command.hasParameter("productCode")) {
|
||||
f.setProductCode(command.stringValueOfParameterNamed("productCode"));
|
||||
present.add("productCode");
|
||||
}
|
||||
if (command.hasParameter("glAccountId")) {
|
||||
f.setGlAccountId(command.longValueOfParameterNamed("glAccountId"));
|
||||
present.add("glAccountId");
|
||||
}
|
||||
if (command.hasParameter("effectiveDate")) {
|
||||
f.setEffectiveDate(command.localDateValueOfParameterNamed("effectiveDate"));
|
||||
present.add("effectiveDate");
|
||||
}
|
||||
if (command.hasParameter("effectiveTo")) {
|
||||
f.setEffectiveTo(command.localDateValueOfParameterNamed("effectiveTo"));
|
||||
present.add("effectiveTo");
|
||||
}
|
||||
if (command.hasParameter("endDate")) {
|
||||
f.setEndDate(command.localDateValueOfParameterNamed("endDate"));
|
||||
present.add("endDate");
|
||||
}
|
||||
if (command.hasParameter("introducerRequired")) {
|
||||
f.setIntroducerRequired(command.booleanObjectValueOfParameterNamed("introducerRequired"));
|
||||
present.add("introducerRequired");
|
||||
}
|
||||
if (command.hasParameter("shareLinkingEnabled")) {
|
||||
f.setShareLinkingEnabled(command.booleanObjectValueOfParameterNamed("shareLinkingEnabled"));
|
||||
present.add("shareLinkingEnabled");
|
||||
}
|
||||
if (command.hasParameter("nominationFacility")) {
|
||||
f.setNominationFacility(command.booleanObjectValueOfParameterNamed("nominationFacility"));
|
||||
present.add("nominationFacility");
|
||||
}
|
||||
if (command.hasParameter("dormancyAlert")) {
|
||||
f.setDormancyAlert(command.booleanObjectValueOfParameterNamed("dormancyAlert"));
|
||||
present.add("dormancyAlert");
|
||||
}
|
||||
if (command.hasParameter("interestCalculateSelector")) {
|
||||
f.setInterestCalculateSelector(command.integerValueOfParameterNamed("interestCalculateSelector"));
|
||||
present.add("interestCalculateSelector");
|
||||
}
|
||||
if (command.hasParameter("averagePeriod")) {
|
||||
f.setAveragePeriod(command.stringValueOfParameterNamed("averagePeriod"));
|
||||
present.add("averagePeriod");
|
||||
}
|
||||
if (command.hasParameter("autoPost")) {
|
||||
f.setAutoPost(command.booleanObjectValueOfParameterNamed("autoPost"));
|
||||
present.add("autoPost");
|
||||
}
|
||||
if (command.hasParameter("frequencyFixDay")) {
|
||||
f.setFrequencyFixDay(command.integerValueOfParameterNamed("frequencyFixDay"));
|
||||
present.add("frequencyFixDay");
|
||||
}
|
||||
if (command.hasParameter("lastInterestDate")) {
|
||||
f.setLastInterestDate(command.localDateValueOfParameterNamed("lastInterestDate"));
|
||||
present.add("lastInterestDate");
|
||||
}
|
||||
if (command.hasParameter("enableLockinPeriod")) {
|
||||
f.setEnableLockinPeriod(command.booleanObjectValueOfParameterNamed("enableLockinPeriod"));
|
||||
present.add("enableLockinPeriod");
|
||||
}
|
||||
if (command.hasParameter("balanceFrequency")) {
|
||||
f.setBalanceFrequency(command.stringValueOfParameterNamed("balanceFrequency"));
|
||||
present.add("balanceFrequency");
|
||||
}
|
||||
if (command.hasParameter("accountClosedZeroBalanceInDayClosing")) {
|
||||
f.setAccountClosedZeroBalanceInDayClosing(command.booleanObjectValueOfParameterNamed("accountClosedZeroBalanceInDayClosing"));
|
||||
present.add("accountClosedZeroBalanceInDayClosing");
|
||||
}
|
||||
if (command.hasParameter("balanceSwapOption")) {
|
||||
f.setBalanceSwapOption(command.booleanObjectValueOfParameterNamed("balanceSwapOption"));
|
||||
present.add("balanceSwapOption");
|
||||
}
|
||||
if (command.hasParameter("msme")) {
|
||||
f.setMsme(command.booleanObjectValueOfParameterNamed("msme"));
|
||||
present.add("msme");
|
||||
}
|
||||
if (command.hasParameter("autoDebitFacility")) {
|
||||
f.setAutoDebitFacility(command.booleanObjectValueOfParameterNamed("autoDebitFacility"));
|
||||
present.add("autoDebitFacility");
|
||||
}
|
||||
if (command.hasParameter("loanFacility")) {
|
||||
f.setLoanFacility(command.booleanObjectValueOfParameterNamed("loanFacility"));
|
||||
present.add("loanFacility");
|
||||
}
|
||||
if (command.hasParameter("prematureWithdrawalOption")) {
|
||||
f.setPrematureWithdrawalOption(command.booleanObjectValueOfParameterNamed("prematureWithdrawalOption"));
|
||||
present.add("prematureWithdrawalOption");
|
||||
}
|
||||
if (command.hasParameter("tenureFlexibility")) {
|
||||
f.setTenureFlexibility(command.booleanObjectValueOfParameterNamed("tenureFlexibility"));
|
||||
present.add("tenureFlexibility");
|
||||
}
|
||||
if (command.hasParameter("lienMarked")) {
|
||||
f.setLienMarked(command.booleanObjectValueOfParameterNamed("lienMarked"));
|
||||
present.add("lienMarked");
|
||||
}
|
||||
if (command.hasParameter("maturityInstructions")) {
|
||||
f.setMaturityInstructions(command.booleanObjectValueOfParameterNamed("maturityInstructions"));
|
||||
present.add("maturityInstructions");
|
||||
}
|
||||
if (command.hasParameter("creditMaturityProceeds")) {
|
||||
f.setCreditMaturityProceeds(command.booleanObjectValueOfParameterNamed("creditMaturityProceeds"));
|
||||
present.add("creditMaturityProceeds");
|
||||
}
|
||||
if (command.hasParameter("penaltyGracePeriodMissedInstallments")) {
|
||||
f.setPenaltyGracePeriodMissedInstallments(command.booleanObjectValueOfParameterNamed("penaltyGracePeriodMissedInstallments"));
|
||||
present.add("penaltyGracePeriodMissedInstallments");
|
||||
}
|
||||
if (command.hasParameter("panelRequestApplicable")) {
|
||||
f.setPanelRequestApplicable(command.booleanObjectValueOfParameterNamed("panelRequestApplicable"));
|
||||
present.add("panelRequestApplicable");
|
||||
}
|
||||
if (command.hasParameter("noPenaltyNomineeHeirClosure")) {
|
||||
f.setNoPenaltyNomineeHeirClosure(command.booleanObjectValueOfParameterNamed("noPenaltyNomineeHeirClosure"));
|
||||
present.add("noPenaltyNomineeHeirClosure");
|
||||
}
|
||||
if (command.hasParameter("deceasedSettlement")) {
|
||||
f.setDeceasedSettlement(command.booleanObjectValueOfParameterNamed("deceasedSettlement"));
|
||||
present.add("deceasedSettlement");
|
||||
}
|
||||
if (command.hasParameter("allowDebitTxnAfterDueDate")) {
|
||||
f.setAllowDebitTxnAfterDueDate(command.booleanObjectValueOfParameterNamed("allowDebitTxnAfterDueDate"));
|
||||
present.add("allowDebitTxnAfterDueDate");
|
||||
}
|
||||
if (command.hasParameter("depositTypeEnum")) {
|
||||
f.setDepositTypeEnum(command.integerValueOfParameterNamed("depositTypeEnum"));
|
||||
present.add("depositTypeEnum");
|
||||
}
|
||||
if (command.hasParameter("withdrawalFeeAmount")) {
|
||||
f.setWithdrawalFeeAmount(command.bigDecimalValueOfParameterNamed("withdrawalFeeAmount"));
|
||||
present.add("withdrawalFeeAmount");
|
||||
}
|
||||
if (command.hasParameter("withdrawalFeeTypeEnum")) {
|
||||
f.setWithdrawalFeeTypeEnum(command.integerValueOfParameterNamed("withdrawalFeeTypeEnum"));
|
||||
present.add("withdrawalFeeTypeEnum");
|
||||
}
|
||||
return present;
|
||||
}
|
||||
|
||||
public static void writeCoreFieldsToJson(final JsonObject root, final SavingsCoreEditFields f, final Set<String> submitted) {
|
||||
if (f == null) {
|
||||
return;
|
||||
}
|
||||
putString(root, submitted, "name", f.getName());
|
||||
putString(root, submitted, "shortName", f.getShortName());
|
||||
putString(root, submitted, "description", f.getDescription());
|
||||
putString(root, submitted, "currencyCode", f.getCurrencyCode());
|
||||
putNumber(root, submitted, "digitsAfterDecimal", f.getDigitsAfterDecimal());
|
||||
putNumber(root, submitted, "inMultiplesOf", f.getInMultiplesOf());
|
||||
putNumber(root, submitted, "accountingRule", f.getAccountingRule());
|
||||
putNumber(root, submitted, "nominalAnnualInterestRate", f.getNominalAnnualInterestRate());
|
||||
putNumber(root, submitted, "interestCompoundingPeriodType", f.getInterestCompoundingPeriodType());
|
||||
putNumber(root, submitted, "interestPostingPeriodType", f.getInterestPostingPeriodType());
|
||||
putNumber(root, submitted, "interestCalculationType", f.getInterestCalculationType());
|
||||
putNumber(root, submitted, "interestCalculationDaysInYearType", f.getInterestCalculationDaysInYearType());
|
||||
putNumber(root, submitted, "minRequiredOpeningBalance", f.getMinRequiredOpeningBalance());
|
||||
putNumber(root, submitted, "lockinPeriodFrequency", f.getLockinPeriodFrequency());
|
||||
putNumber(root, submitted, "lockinPeriodFrequencyType", f.getLockinPeriodFrequencyType());
|
||||
putBoolean(root, submitted, "withdrawalFeeForTransfers", f.getWithdrawalFeeForTransfers());
|
||||
putBoolean(root, submitted, "allowOverdraft", f.getAllowOverdraft());
|
||||
putNumber(root, submitted, "overdraftLimit", f.getOverdraftLimit());
|
||||
putNumber(root, submitted, "nominalAnnualInterestRateOverdraft", f.getNominalAnnualInterestRateOverdraft());
|
||||
putNumber(root, submitted, "minOverdraftForInterestCalculation", f.getMinOverdraftForInterestCalculation());
|
||||
putBoolean(root, submitted, "enforceMinRequiredBalance", f.getEnforceMinRequiredBalance());
|
||||
putNumber(root, submitted, "minRequiredBalance", f.getMinRequiredBalance());
|
||||
putBoolean(root, submitted, "lienAllowed", f.getLienAllowed());
|
||||
putNumber(root, submitted, "maxAllowedLienLimit", f.getMaxAllowedLienLimit());
|
||||
putNumber(root, submitted, "minBalanceForInterestCalculation", f.getMinBalanceForInterestCalculation());
|
||||
putBoolean(root, submitted, "withHoldTax", f.getWithHoldTax());
|
||||
putNumber(root, submitted, "taxGroupId", f.getTaxGroupId());
|
||||
putString(root, submitted, "otherGlMappings", f.getOtherGlMappings());
|
||||
putBoolean(root, submitted, "isDormancyTrackingActive", f.getIsDormancyTrackingActive());
|
||||
putNumber(root, submitted, "daysToInactive", f.getDaysToInactive());
|
||||
putNumber(root, submitted, "daysToDormancy", f.getDaysToDormancy());
|
||||
putNumber(root, submitted, "daysToEscheat", f.getDaysToEscheat());
|
||||
putString(root, submitted, "accountType", f.getAccountType());
|
||||
putString(root, submitted, "customerSegment", f.getCustomerSegment());
|
||||
putString(root, submitted, "productCode", f.getProductCode());
|
||||
putNumber(root, submitted, "glAccountId", f.getGlAccountId());
|
||||
putDate(root, submitted, "effectiveDate", f.getEffectiveDate());
|
||||
putDate(root, submitted, "effectiveTo", f.getEffectiveTo());
|
||||
putDate(root, submitted, "endDate", f.getEndDate());
|
||||
putBoolean(root, submitted, "introducerRequired", f.getIntroducerRequired());
|
||||
putBoolean(root, submitted, "shareLinkingEnabled", f.getShareLinkingEnabled());
|
||||
putBoolean(root, submitted, "nominationFacility", f.getNominationFacility());
|
||||
putBoolean(root, submitted, "dormancyAlert", f.getDormancyAlert());
|
||||
putNumber(root, submitted, "interestCalculateSelector", f.getInterestCalculateSelector());
|
||||
putString(root, submitted, "averagePeriod", f.getAveragePeriod());
|
||||
putBoolean(root, submitted, "autoPost", f.getAutoPost());
|
||||
putNumber(root, submitted, "frequencyFixDay", f.getFrequencyFixDay());
|
||||
putDate(root, submitted, "lastInterestDate", f.getLastInterestDate());
|
||||
putBoolean(root, submitted, "enableLockinPeriod", f.getEnableLockinPeriod());
|
||||
putString(root, submitted, "balanceFrequency", f.getBalanceFrequency());
|
||||
putBoolean(root, submitted, "accountClosedZeroBalanceInDayClosing", f.getAccountClosedZeroBalanceInDayClosing());
|
||||
putBoolean(root, submitted, "balanceSwapOption", f.getBalanceSwapOption());
|
||||
putBoolean(root, submitted, "msme", f.getMsme());
|
||||
putBoolean(root, submitted, "autoDebitFacility", f.getAutoDebitFacility());
|
||||
putBoolean(root, submitted, "loanFacility", f.getLoanFacility());
|
||||
putBoolean(root, submitted, "prematureWithdrawalOption", f.getPrematureWithdrawalOption());
|
||||
putBoolean(root, submitted, "tenureFlexibility", f.getTenureFlexibility());
|
||||
putBoolean(root, submitted, "lienMarked", f.getLienMarked());
|
||||
putBoolean(root, submitted, "maturityInstructions", f.getMaturityInstructions());
|
||||
putBoolean(root, submitted, "creditMaturityProceeds", f.getCreditMaturityProceeds());
|
||||
putBoolean(root, submitted, "penaltyGracePeriodMissedInstallments", f.getPenaltyGracePeriodMissedInstallments());
|
||||
putBoolean(root, submitted, "panelRequestApplicable", f.getPanelRequestApplicable());
|
||||
putBoolean(root, submitted, "noPenaltyNomineeHeirClosure", f.getNoPenaltyNomineeHeirClosure());
|
||||
putBoolean(root, submitted, "deceasedSettlement", f.getDeceasedSettlement());
|
||||
putBoolean(root, submitted, "allowDebitTxnAfterDueDate", f.getAllowDebitTxnAfterDueDate());
|
||||
putNumber(root, submitted, "depositTypeEnum", f.getDepositTypeEnum());
|
||||
putNumber(root, submitted, "withdrawalFeeAmount", f.getWithdrawalFeeAmount());
|
||||
putNumber(root, submitted, "withdrawalFeeTypeEnum", f.getWithdrawalFeeTypeEnum());
|
||||
}
|
||||
|
||||
// ---- accountFacilityConfig (1:1 container) --------------------------------------------------
|
||||
|
||||
public static boolean populateFacilityConfig(final JsonCommand command, final FacilityConfigEditFields f) {
|
||||
if (!command.hasParameter("accountFacilityConfig")) {
|
||||
return false;
|
||||
}
|
||||
f.setAtmFacility(booleanOf(command, "accountFacilityConfig", "atmFacility"));
|
||||
f.setAtmWithdrawalFacility(booleanOf(command, "accountFacilityConfig", "atmWithdrawalFacility"));
|
||||
f.setUpiFacility(booleanOf(command, "accountFacilityConfig", "upiFacility"));
|
||||
f.setPosTransactionsFacility(booleanOf(command, "accountFacilityConfig", "posTransactionsFacility"));
|
||||
f.setChequeBook(booleanOf(command, "accountFacilityConfig", "chequeBook"));
|
||||
f.setImpsFacility(booleanOf(command, "accountFacilityConfig", "impsFacility"));
|
||||
f.setSmsAlerts(booleanOf(command, "accountFacilityConfig", "smsAlerts"));
|
||||
f.setBbpsFacility(booleanOf(command, "accountFacilityConfig", "bbpsFacility"));
|
||||
f.setInsuranceCover(booleanOf(command, "accountFacilityConfig", "insuranceCover"));
|
||||
f.setBepgFacility(booleanOf(command, "accountFacilityConfig", "bepgFacility"));
|
||||
f.setNeftFacility(booleanOf(command, "accountFacilityConfig", "neftFacility"));
|
||||
f.setRtgsFacility(booleanOf(command, "accountFacilityConfig", "rtgsFacility"));
|
||||
f.setInterBankTotalCapFacility(booleanOf(command, "accountFacilityConfig", "interBankTotalCapFacility"));
|
||||
f.setWithdrawalFacility(booleanOf(command, "accountFacilityConfig", "withdrawalFacility"));
|
||||
f.setDepositFacilityKyc(booleanOf(command, "accountFacilityConfig", "depositFacilityKyc"));
|
||||
f.setVirtualDebitCard(booleanOf(command, "accountFacilityConfig", "virtualDebitCard"));
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void writeFacilityConfigToJson(final JsonObject root, final FacilityConfigEditFields f, final boolean submitted) {
|
||||
if (f == null || !submitted) {
|
||||
return;
|
||||
}
|
||||
final JsonObject config = new JsonObject();
|
||||
config.addProperty("atmFacility", f.getAtmFacility());
|
||||
config.addProperty("atmWithdrawalFacility", f.getAtmWithdrawalFacility());
|
||||
config.addProperty("upiFacility", f.getUpiFacility());
|
||||
config.addProperty("posTransactionsFacility", f.getPosTransactionsFacility());
|
||||
config.addProperty("chequeBook", f.getChequeBook());
|
||||
config.addProperty("impsFacility", f.getImpsFacility());
|
||||
config.addProperty("smsAlerts", f.getSmsAlerts());
|
||||
config.addProperty("bbpsFacility", f.getBbpsFacility());
|
||||
config.addProperty("insuranceCover", f.getInsuranceCover());
|
||||
config.addProperty("bepgFacility", f.getBepgFacility());
|
||||
config.addProperty("neftFacility", f.getNeftFacility());
|
||||
config.addProperty("rtgsFacility", f.getRtgsFacility());
|
||||
config.addProperty("interBankTotalCapFacility", f.getInterBankTotalCapFacility());
|
||||
config.addProperty("withdrawalFacility", f.getWithdrawalFacility());
|
||||
config.addProperty("depositFacilityKyc", f.getDepositFacilityKyc());
|
||||
config.addProperty("virtualDebitCard", f.getVirtualDebitCard());
|
||||
root.add("accountFacilityConfig", config);
|
||||
}
|
||||
|
||||
// ---- depositLimitKyc (1:1 container, nested general/minor/smallSaving shape) ----------------
|
||||
|
||||
public static boolean populateDepositLimitKyc(final JsonCommand command, final DepositLimitKycEditFields f) {
|
||||
if (!command.hasParameter("depositLimitKyc")) {
|
||||
return false;
|
||||
}
|
||||
final com.google.gson.JsonElement raw = command.parsedJson().getAsJsonObject().get("depositLimitKyc");
|
||||
if (raw == null || !raw.isJsonObject()) {
|
||||
return true;
|
||||
}
|
||||
final JsonObject kyc = raw.getAsJsonObject();
|
||||
if (kyc.has("general") && kyc.get("general").isJsonObject()) {
|
||||
final JsonObject general = kyc.getAsJsonObject("general");
|
||||
f.setGeneralType(stringOrNull(general, "kycType"));
|
||||
f.setGeneralLimit(stringOrNull(general, "limit"));
|
||||
}
|
||||
if (kyc.has("minor") && kyc.get("minor").isJsonObject()) {
|
||||
final JsonObject minor = kyc.getAsJsonObject("minor");
|
||||
f.setMinorType(stringOrNull(minor, "kycType"));
|
||||
final String minorLimit = stringOrNull(minor, "limit");
|
||||
f.setMinorLimit(minorLimit == null ? null : Long.valueOf(minorLimit));
|
||||
}
|
||||
if (kyc.has("smallSaving") && kyc.get("smallSaving").isJsonObject()) {
|
||||
final JsonObject smallSaving = kyc.getAsJsonObject("smallSaving");
|
||||
f.setSmallSavingType(stringOrNull(smallSaving, "kycType"));
|
||||
final String smallSavingLimit = stringOrNull(smallSaving, "limit");
|
||||
f.setSmallSavingLimit(smallSavingLimit == null ? null : Long.valueOf(smallSavingLimit));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void writeDepositLimitKycToJson(final JsonObject root, final DepositLimitKycEditFields f, final boolean submitted) {
|
||||
if (f == null || !submitted) {
|
||||
return;
|
||||
}
|
||||
final JsonObject kyc = new JsonObject();
|
||||
final JsonObject general = new JsonObject();
|
||||
general.addProperty("kycType", f.getGeneralType());
|
||||
general.addProperty("limit", f.getGeneralLimit());
|
||||
kyc.add("general", general);
|
||||
final JsonObject minor = new JsonObject();
|
||||
minor.addProperty("kycType", f.getMinorType());
|
||||
if (f.getMinorLimit() != null) {
|
||||
minor.addProperty("limit", f.getMinorLimit());
|
||||
}
|
||||
kyc.add("minor", minor);
|
||||
final JsonObject smallSaving = new JsonObject();
|
||||
smallSaving.addProperty("kycType", f.getSmallSavingType());
|
||||
if (f.getSmallSavingLimit() != null) {
|
||||
smallSaving.addProperty("limit", f.getSmallSavingLimit());
|
||||
}
|
||||
kyc.add("smallSaving", smallSaving);
|
||||
root.add("depositLimitKyc", kyc);
|
||||
}
|
||||
|
||||
// ---- GL scalar account mapping (1:1) ----------------------------------------------------------
|
||||
|
||||
public static boolean populateGlAccountMapping(final JsonCommand command, final GlAccountMappingEditFields f) {
|
||||
boolean any = false;
|
||||
any |= setIfPresent(command, "savingsReferenceAccountId", f::setSavingsReferenceAccountId);
|
||||
any |= setIfPresent(command, "savingsControlAccountId", f::setSavingsControlAccountId);
|
||||
any |= setIfPresent(command, "incomeFromFeeAccountId", f::setIncomeFromFeeAccountId);
|
||||
any |= setIfPresent(command, "incomeFromPenaltyAccountId", f::setIncomeFromPenaltyAccountId);
|
||||
any |= setIfPresent(command, "interestOnSavingsAccountId", f::setInterestOnSavingsAccountId);
|
||||
any |= setIfPresent(command, "transfersInSuspenseAccountId", f::setTransfersInSuspenseAccountId);
|
||||
any |= setIfPresent(command, "overdraftPortfolioControlId", f::setOverdraftPortfolioControlId);
|
||||
any |= setIfPresent(command, "incomeFromInterestId", f::setIncomeFromInterestId);
|
||||
any |= setIfPresent(command, "writeOffAccountId", f::setWriteOffAccountId);
|
||||
any |= setIfPresent(command, "escheatLiabilityId", f::setEscheatLiabilityId);
|
||||
any |= setIfPresent(command, "penaltiesReceivableAccountId", f::setPenaltiesReceivableAccountId);
|
||||
any |= setIfPresent(command, "interestReceivableAccountId", f::setInterestReceivableAccountId);
|
||||
any |= setIfPresent(command, "feesReceivableAccountId", f::setFeesReceivableAccountId);
|
||||
any |= setIfPresent(command, "interestPayableAccountId", f::setInterestPayableAccountId);
|
||||
return any;
|
||||
}
|
||||
|
||||
public static void writeGlAccountMappingToJson(final JsonObject root, final GlAccountMappingEditFields f) {
|
||||
if (f == null) {
|
||||
return;
|
||||
}
|
||||
putNumber(root, "savingsReferenceAccountId", f.getSavingsReferenceAccountId());
|
||||
putNumber(root, "savingsControlAccountId", f.getSavingsControlAccountId());
|
||||
putNumber(root, "incomeFromFeeAccountId", f.getIncomeFromFeeAccountId());
|
||||
putNumber(root, "incomeFromPenaltyAccountId", f.getIncomeFromPenaltyAccountId());
|
||||
putNumber(root, "interestOnSavingsAccountId", f.getInterestOnSavingsAccountId());
|
||||
putNumber(root, "transfersInSuspenseAccountId", f.getTransfersInSuspenseAccountId());
|
||||
putNumber(root, "overdraftPortfolioControlId", f.getOverdraftPortfolioControlId());
|
||||
putNumber(root, "incomeFromInterestId", f.getIncomeFromInterestId());
|
||||
putNumber(root, "writeOffAccountId", f.getWriteOffAccountId());
|
||||
putNumber(root, "escheatLiabilityId", f.getEscheatLiabilityId());
|
||||
putNumber(root, "penaltiesReceivableAccountId", f.getPenaltiesReceivableAccountId());
|
||||
putNumber(root, "interestReceivableAccountId", f.getInterestReceivableAccountId());
|
||||
putNumber(root, "feesReceivableAccountId", f.getFeesReceivableAccountId());
|
||||
putNumber(root, "interestPayableAccountId", f.getInterestPayableAccountId());
|
||||
}
|
||||
|
||||
// ---- charges (1:many, satellite table) --------------------------------------------------------
|
||||
|
||||
/** Returns null if "charges" wasn't present at all, else the (possibly empty) list of charge ids. */
|
||||
public static List<Long> extractChargeIds(final JsonCommand command) {
|
||||
if (!command.hasParameter("charges")) {
|
||||
return null;
|
||||
}
|
||||
final com.google.gson.JsonElement raw = command.parsedJson().getAsJsonObject().get("charges");
|
||||
final List<Long> ids = new java.util.ArrayList<>();
|
||||
if (raw != null && raw.isJsonArray()) {
|
||||
for (final com.google.gson.JsonElement el : raw.getAsJsonArray()) {
|
||||
if (el.isJsonObject() && el.getAsJsonObject().has("id")) {
|
||||
ids.add(el.getAsJsonObject().get("id").getAsLong());
|
||||
}
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
public static void writeChargesToJson(final JsonObject root, final List<ProductPendingEditCharge> charges) {
|
||||
final JsonArray array = new JsonArray();
|
||||
for (final ProductPendingEditCharge charge : charges) {
|
||||
final JsonObject entry = new JsonObject();
|
||||
entry.addProperty("id", charge.getChargeId());
|
||||
array.add(entry);
|
||||
}
|
||||
root.add("charges", array);
|
||||
}
|
||||
|
||||
// ---- accountFacilityLimits (1:many, satellite table) -------------------------------------------
|
||||
|
||||
public static List<ProductPendingEditFacilityLimit> extractFacilityLimits(final JsonCommand command, final ProductPendingEdit edit) {
|
||||
if (!command.hasParameter("accountFacilityLimits")) {
|
||||
return null;
|
||||
}
|
||||
final com.google.gson.JsonElement raw = command.parsedJson().getAsJsonObject().get("accountFacilityLimits");
|
||||
final List<ProductPendingEditFacilityLimit> limits = new java.util.ArrayList<>();
|
||||
if (raw != null && raw.isJsonArray()) {
|
||||
for (final com.google.gson.JsonElement el : raw.getAsJsonArray()) {
|
||||
final JsonObject o = el.getAsJsonObject();
|
||||
limits.add(new ProductPendingEditFacilityLimit(edit, stringOrNull(o, "limitType"), stringOrNull(o, "generalLimit"),
|
||||
stringOrNull(o, "minorAccountLimit"), stringOrNull(o, "smallSavingLimit")));
|
||||
}
|
||||
}
|
||||
return limits;
|
||||
}
|
||||
|
||||
public static void writeFacilityLimitsToJson(final JsonObject root, final List<ProductPendingEditFacilityLimit> limits) {
|
||||
final JsonArray array = new JsonArray();
|
||||
for (final ProductPendingEditFacilityLimit limit : limits) {
|
||||
final JsonObject entry = new JsonObject();
|
||||
entry.addProperty("limitType", limit.getLimitType());
|
||||
entry.addProperty("generalLimit", limit.getGeneralLimit());
|
||||
entry.addProperty("minorAccountLimit", limit.getMinorAccountLimit());
|
||||
entry.addProperty("smallSavingLimit", limit.getSmallSavingLimit());
|
||||
array.add(entry);
|
||||
}
|
||||
root.add("accountFacilityLimits", array);
|
||||
}
|
||||
|
||||
// ---- GL array mappings (1:many, satellite table) -----------------------------------------------
|
||||
|
||||
public static List<ProductPendingEditGlMapping> extractGlArrayMappings(final JsonCommand command, final ProductPendingEdit edit) {
|
||||
final List<ProductPendingEditGlMapping> mappings = new java.util.ArrayList<>();
|
||||
boolean any = false;
|
||||
any |= extractGlArray(command, edit, "paymentChannelToFundSourceMappings", ProductPendingEditGlMapping.TYPE_PAYMENT_CHANNEL,
|
||||
"paymentTypeId", mappings);
|
||||
any |= extractGlArray(command, edit, "feeToIncomeAccountMappings", ProductPendingEditGlMapping.TYPE_FEE, "chargeId", mappings);
|
||||
any |= extractGlArray(command, edit, "penaltyToIncomeAccountMappings", ProductPendingEditGlMapping.TYPE_PENALTY, "chargeId",
|
||||
mappings);
|
||||
return any ? mappings : null;
|
||||
}
|
||||
|
||||
private static boolean extractGlArray(final JsonCommand command, final ProductPendingEdit edit, final String paramName,
|
||||
final String mappingType, final String idFieldName, final List<ProductPendingEditGlMapping> out) {
|
||||
if (!command.hasParameter(paramName)) {
|
||||
return false;
|
||||
}
|
||||
final com.google.gson.JsonElement raw = command.parsedJson().getAsJsonObject().get(paramName);
|
||||
if (raw != null && raw.isJsonArray()) {
|
||||
for (final com.google.gson.JsonElement el : raw.getAsJsonArray()) {
|
||||
final JsonObject o = el.getAsJsonObject();
|
||||
final Long idFieldValue = o.has(idFieldName) ? o.get(idFieldName).getAsLong() : null;
|
||||
final Long glAccountId = o.has("glAccountId") ? o.get("glAccountId").getAsLong() : null;
|
||||
final Long paymentTypeId = "paymentTypeId".equals(idFieldName) ? idFieldValue : null;
|
||||
final Long chargeId = "chargeId".equals(idFieldName) ? idFieldValue : null;
|
||||
out.add(new ProductPendingEditGlMapping(edit, mappingType, paymentTypeId, chargeId, glAccountId));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void writeGlArrayMappingsToJson(final JsonObject root, final List<ProductPendingEditGlMapping> mappings) {
|
||||
writeGlArrayGroup(root, mappings, ProductPendingEditGlMapping.TYPE_PAYMENT_CHANNEL, "paymentChannelToFundSourceMappings",
|
||||
"paymentTypeId");
|
||||
writeGlArrayGroup(root, mappings, ProductPendingEditGlMapping.TYPE_FEE, "feeToIncomeAccountMappings", "chargeId");
|
||||
writeGlArrayGroup(root, mappings, ProductPendingEditGlMapping.TYPE_PENALTY, "penaltyToIncomeAccountMappings", "chargeId");
|
||||
}
|
||||
|
||||
private static void writeGlArrayGroup(final JsonObject root, final List<ProductPendingEditGlMapping> mappings, final String type,
|
||||
final String paramName, final String idFieldName) {
|
||||
final JsonArray array = new JsonArray();
|
||||
for (final ProductPendingEditGlMapping mapping : mappings) {
|
||||
if (!type.equals(mapping.getMappingType())) {
|
||||
continue;
|
||||
}
|
||||
final JsonObject entry = new JsonObject();
|
||||
final Long idFieldValue = "paymentTypeId".equals(idFieldName) ? mapping.getPaymentTypeId() : mapping.getChargeId();
|
||||
entry.addProperty(idFieldName, idFieldValue);
|
||||
entry.addProperty("glAccountId", mapping.getGlAccountId());
|
||||
array.add(entry);
|
||||
}
|
||||
root.add(paramName, array);
|
||||
}
|
||||
|
||||
// ---- small helpers -----------------------------------------------------------------------------
|
||||
|
||||
private static boolean booleanOf(final JsonCommand command, final String containerName, final String fieldName) {
|
||||
final com.google.gson.JsonElement raw = command.parsedJson().getAsJsonObject().get(containerName);
|
||||
if (raw == null || !raw.isJsonObject()) {
|
||||
return false;
|
||||
}
|
||||
final JsonObject container = raw.getAsJsonObject();
|
||||
return container.has(fieldName) && !container.get(fieldName).isJsonNull() && container.get(fieldName).getAsBoolean();
|
||||
}
|
||||
|
||||
private static String stringOrNull(final JsonObject o, final String field) {
|
||||
return (o.has(field) && !o.get(field).isJsonNull()) ? o.get(field).getAsString() : null;
|
||||
}
|
||||
|
||||
private interface LongSetter {
|
||||
|
||||
void set(Long value);
|
||||
}
|
||||
|
||||
private static boolean setIfPresent(final JsonCommand command, final String paramName, final LongSetter setter) {
|
||||
if (!command.hasParameter(paramName)) {
|
||||
return false;
|
||||
}
|
||||
setter.set(command.longValueOfParameterNamed(paramName));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void putString(final JsonObject root, final Set<String> submitted, final String name, final String value) {
|
||||
if (submitted.contains(name)) {
|
||||
root.addProperty(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void putNumber(final JsonObject root, final Set<String> submitted, final String name, final Number value) {
|
||||
if (submitted.contains(name)) {
|
||||
root.addProperty(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void putBoolean(final JsonObject root, final Set<String> submitted, final String name, final Boolean value) {
|
||||
if (submitted.contains(name)) {
|
||||
root.addProperty(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void putDate(final JsonObject root, final Set<String> submitted, final String name, final LocalDate value) {
|
||||
if (submitted.contains(name)) {
|
||||
root.addProperty(name, value == null ? null : value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void putNumber(final JsonObject root, final String name, final Number value) {
|
||||
if (value != null) {
|
||||
root.addProperty(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<String> populateCurrentOnlyFields(final JsonCommand command, final CurrentOnlyEditFields f) {
|
||||
final Set<String> present = new java.util.HashSet<>();
|
||||
if (command.hasParameter("avgBalanceCalcPeriod")) {
|
||||
f.setAvgBalanceCalcPeriod(command.stringValueOfParameterNamed("avgBalanceCalcPeriod"));
|
||||
present.add("avgBalanceCalcPeriod");
|
||||
}
|
||||
if (command.hasParameter("validForPeriod")) {
|
||||
f.setValidForPeriod(command.stringValueOfParameterNamed("validForPeriod"));
|
||||
present.add("validForPeriod");
|
||||
}
|
||||
if (command.hasParameter("odAccountClosedZeroBalanceInDayClosing")) {
|
||||
f.setOdAccountClosedZeroBalanceInDayClosing(command.booleanObjectValueOfParameterNamed("odAccountClosedZeroBalanceInDayClosing"));
|
||||
present.add("odAccountClosedZeroBalanceInDayClosing");
|
||||
}
|
||||
return present;
|
||||
}
|
||||
|
||||
public static void writeCurrentOnlyFieldsToJson(final JsonObject root, final CurrentOnlyEditFields f, final Set<String> submitted) {
|
||||
if (f == null) {
|
||||
return;
|
||||
}
|
||||
putString(root, submitted, "avgBalanceCalcPeriod", f.getAvgBalanceCalcPeriod());
|
||||
putString(root, submitted, "validForPeriod", f.getValidForPeriod());
|
||||
putBoolean(root, submitted, "odAccountClosedZeroBalanceInDayClosing", f.getOdAccountClosedZeroBalanceInDayClosing());
|
||||
}
|
||||
|
||||
public static Set<String> populateShareOnlyFields(final JsonCommand command, final ShareOnlyEditFields f) {
|
||||
final Set<String> present = new java.util.HashSet<>();
|
||||
if (command.hasParameter("totalShares")) {
|
||||
f.setTotalShares(command.longValueOfParameterNamed("totalShares"));
|
||||
present.add("totalShares");
|
||||
}
|
||||
if (command.hasParameter("totalSharesIssued") || command.hasParameter("issuedShares")) {
|
||||
final Long val = command.hasParameter("totalSharesIssued") ? command.longValueOfParameterNamed("totalSharesIssued") : command.longValueOfParameterNamed("issuedShares");
|
||||
f.setTotalSharesIssued(val);
|
||||
present.add("totalSharesIssued");
|
||||
}
|
||||
if (command.hasParameter("unitPrice")) {
|
||||
f.setUnitPrice(command.bigDecimalValueOfParameterNamed("unitPrice"));
|
||||
present.add("unitPrice");
|
||||
}
|
||||
if (command.hasParameter("minimumShares") || command.hasParameter("minimumClientShares")) {
|
||||
final Long val = command.hasParameter("minimumShares") ? command.longValueOfParameterNamed("minimumShares") : command.longValueOfParameterNamed("minimumClientShares");
|
||||
f.setMinimumShares(val);
|
||||
present.add("minimumShares");
|
||||
}
|
||||
if (command.hasParameter("nominalShares") || command.hasParameter("nominalClientShares")) {
|
||||
final Long val = command.hasParameter("nominalShares") ? command.longValueOfParameterNamed("nominalShares") : command.longValueOfParameterNamed("nominalClientShares");
|
||||
f.setNominalShares(val);
|
||||
present.add("nominalShares");
|
||||
}
|
||||
if (command.hasParameter("maximumShares") || command.hasParameter("maximumClientShares")) {
|
||||
final Long val = command.hasParameter("maximumShares") ? command.longValueOfParameterNamed("maximumShares") : command.longValueOfParameterNamed("maximumClientShares");
|
||||
f.setMaximumShares(val);
|
||||
present.add("maximumShares");
|
||||
}
|
||||
if (command.hasParameter("allowDividendCalcForInactiveClients") || command.hasParameter("allowDividendsInactiveClients")) {
|
||||
final Boolean val = command.hasParameter("allowDividendCalcForInactiveClients") ? command.booleanObjectValueOfParameterNamed("allowDividendCalcForInactiveClients") : command.booleanObjectValueOfParameterNamed("allowDividendsInactiveClients");
|
||||
f.setAllowDividendCalcForInactiveClients(val);
|
||||
present.add("allowDividendCalcForInactiveClients");
|
||||
}
|
||||
if (command.hasParameter("minimumActivePeriodForDividends") || command.hasParameter("minimumActivePeriodFrequency")) {
|
||||
final Integer val = command.hasParameter("minimumActivePeriodForDividends") ? command.integerValueOfParameterNamed("minimumActivePeriodForDividends") : command.integerValueOfParameterNamed("minimumActivePeriodFrequency");
|
||||
f.setMinimumActivePeriodForDividends(val);
|
||||
present.add("minimumActivePeriodForDividends");
|
||||
}
|
||||
if (command.hasParameter("minimumActivePeriodFrequencyType") || command.hasParameter("minimumActivePeriodFrequencyEnum")) {
|
||||
final Integer val = command.hasParameter("minimumActivePeriodFrequencyType") ? command.integerValueOfParameterNamed("minimumActivePeriodFrequencyType") : command.integerValueOfParameterNamed("minimumActivePeriodFrequencyEnum");
|
||||
f.setMinimumActivePeriodFrequencyType(val);
|
||||
present.add("minimumActivePeriodFrequencyType");
|
||||
}
|
||||
if (command.hasParameter("shareLockinPeriod")) {
|
||||
f.setShareLockinPeriod(command.integerValueOfParameterNamed("shareLockinPeriod"));
|
||||
present.add("shareLockinPeriod");
|
||||
}
|
||||
if (command.hasParameter("shareLockinPeriodFrequencyType")) {
|
||||
f.setShareLockinPeriodFrequencyType(command.integerValueOfParameterNamed("shareLockinPeriodFrequencyType"));
|
||||
present.add("shareLockinPeriodFrequencyType");
|
||||
}
|
||||
if (command.hasParameter("externalId")) {
|
||||
f.setExternalId(command.stringValueOfParameterNamed("externalId"));
|
||||
present.add("externalId");
|
||||
}
|
||||
if (command.hasParameter("startDate")) {
|
||||
f.setStartDate(command.localDateValueOfParameterNamed("startDate"));
|
||||
present.add("startDate");
|
||||
}
|
||||
if (command.hasParameter("totalsubscribedShares")) {
|
||||
f.setTotalsubscribedShares(command.longValueOfParameterNamed("totalsubscribedShares"));
|
||||
present.add("totalsubscribedShares");
|
||||
}
|
||||
if (command.hasParameter("capitalAmount") || command.hasParameter("shareCapital")) {
|
||||
final BigDecimal val = command.hasParameter("capitalAmount") ? command.bigDecimalValueOfParameterNamed("capitalAmount") : command.bigDecimalValueOfParameterNamed("shareCapital");
|
||||
f.setCapitalAmount(val);
|
||||
present.add("shareCapital");
|
||||
}
|
||||
return present;
|
||||
}
|
||||
|
||||
public static void writeShareOnlyFieldsToJson(final JsonObject root, final ShareOnlyEditFields f, final Set<String> submitted) {
|
||||
if (f == null) {
|
||||
return;
|
||||
}
|
||||
putNumber(root, submitted, "totalShares", f.getTotalShares());
|
||||
putNumber(root, submitted, "totalSharesIssued", f.getTotalSharesIssued());
|
||||
putNumber(root, submitted, "unitPrice", f.getUnitPrice());
|
||||
putNumber(root, submitted, "minimumShares", f.getMinimumShares());
|
||||
putNumber(root, submitted, "nominalShares", f.getNominalShares());
|
||||
putNumber(root, submitted, "maximumShares", f.getMaximumShares());
|
||||
putBoolean(root, submitted, "allowDividendCalcForInactiveClients", f.getAllowDividendCalcForInactiveClients());
|
||||
putNumber(root, submitted, "minimumActivePeriodForDividends", f.getMinimumActivePeriodForDividends());
|
||||
putNumber(root, submitted, "minimumActivePeriodFrequencyType", f.getMinimumActivePeriodFrequencyType());
|
||||
putNumber(root, submitted, "shareLockinPeriod", f.getShareLockinPeriod());
|
||||
putNumber(root, submitted, "shareLockinPeriodFrequencyType", f.getShareLockinPeriodFrequencyType());
|
||||
putString(root, submitted, "externalId", f.getExternalId());
|
||||
putDate(root, submitted, "startDate", f.getStartDate());
|
||||
putNumber(root, submitted, "totalsubscribedShares", f.getTotalsubscribedShares());
|
||||
if (submitted.contains("capitalAmount") || submitted.contains("shareCapital")) {
|
||||
if (f.getCapitalAmount() != null) {
|
||||
root.addProperty("shareCapital", f.getCapitalAmount());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<String> populateFixedDepositFields(final JsonCommand command, final FixedDepositEditFields f) {
|
||||
final Set<String> present = new java.util.HashSet<>();
|
||||
if (command.hasParameter("fdType")) {
|
||||
f.setFdType(command.stringValueOfParameterNamed("fdType"));
|
||||
present.add("fdType");
|
||||
}
|
||||
if (command.hasParameter("preClosurePenalApplicable")) {
|
||||
f.setPreClosurePenalApplicable(command.booleanObjectValueOfParameterNamed("preClosurePenalApplicable"));
|
||||
present.add("preClosurePenalApplicable");
|
||||
}
|
||||
if (command.hasParameter("preClosurePenalInterest")) {
|
||||
f.setPreClosurePenalInterest(command.bigDecimalValueOfParameterNamed("preClosurePenalInterest"));
|
||||
present.add("preClosurePenalInterest");
|
||||
}
|
||||
if (command.hasParameter("preClosurePenalInterestOnTypeId")) {
|
||||
f.setPreClosurePenalInterestOnTypeId(command.integerValueOfParameterNamed("preClosurePenalInterestOnTypeId"));
|
||||
present.add("preClosurePenalInterestOnTypeId");
|
||||
}
|
||||
if (command.hasParameter("minDepositTerm")) {
|
||||
f.setMinDepositTerm(command.integerValueOfParameterNamed("minDepositTerm"));
|
||||
present.add("minDepositTerm");
|
||||
}
|
||||
if (command.hasParameter("maxDepositTerm")) {
|
||||
f.setMaxDepositTerm(command.integerValueOfParameterNamed("maxDepositTerm"));
|
||||
present.add("maxDepositTerm");
|
||||
}
|
||||
if (command.hasParameter("minDepositTermTypeId")) {
|
||||
f.setMinDepositTermTypeId(command.integerValueOfParameterNamed("minDepositTermTypeId"));
|
||||
present.add("minDepositTermTypeId");
|
||||
}
|
||||
if (command.hasParameter("maxDepositTermTypeId")) {
|
||||
f.setMaxDepositTermTypeId(command.integerValueOfParameterNamed("maxDepositTermTypeId"));
|
||||
present.add("maxDepositTermTypeId");
|
||||
}
|
||||
if (command.hasParameter("inMultiplesOfDepositTerm")) {
|
||||
f.setInMultiplesOfDepositTerm(command.integerValueOfParameterNamed("inMultiplesOfDepositTerm"));
|
||||
present.add("inMultiplesOfDepositTerm");
|
||||
}
|
||||
if (command.hasParameter("inMultiplesOfDepositTermTypeId")) {
|
||||
f.setInMultiplesOfDepositTermTypeId(command.integerValueOfParameterNamed("inMultiplesOfDepositTermTypeId"));
|
||||
present.add("inMultiplesOfDepositTermTypeId");
|
||||
}
|
||||
if (command.hasParameter("minDepositAmount")) {
|
||||
f.setMinDepositAmount(command.bigDecimalValueOfParameterNamed("minDepositAmount"));
|
||||
present.add("minDepositAmount");
|
||||
}
|
||||
if (command.hasParameter("maxDepositAmount")) {
|
||||
f.setMaxDepositAmount(command.bigDecimalValueOfParameterNamed("maxDepositAmount"));
|
||||
present.add("maxDepositAmount");
|
||||
}
|
||||
if (command.hasParameter("depositAmount")) {
|
||||
f.setDepositAmount(command.bigDecimalValueOfParameterNamed("depositAmount"));
|
||||
present.add("depositAmount");
|
||||
}
|
||||
return present;
|
||||
}
|
||||
|
||||
public static void writeFixedDepositFieldsToJson(final JsonObject root, final FixedDepositEditFields f, final Set<String> submitted) {
|
||||
if (f == null) {
|
||||
return;
|
||||
}
|
||||
if (f.getFdType() != null) putString(root, submitted, "fdType", f.getFdType());
|
||||
if (f.getPreClosurePenalApplicable() != null) putBoolean(root, submitted, "preClosurePenalApplicable", f.getPreClosurePenalApplicable());
|
||||
if (f.getPreClosurePenalInterest() != null) putNumber(root, submitted, "preClosurePenalInterest", f.getPreClosurePenalInterest());
|
||||
if (f.getPreClosurePenalInterestOnTypeId() != null) putNumber(root, submitted, "preClosurePenalInterestOnTypeId", f.getPreClosurePenalInterestOnTypeId());
|
||||
if (f.getMinDepositTerm() != null) putNumber(root, submitted, "minDepositTerm", f.getMinDepositTerm());
|
||||
if (f.getMaxDepositTerm() != null) putNumber(root, submitted, "maxDepositTerm", f.getMaxDepositTerm());
|
||||
if (f.getMinDepositTermTypeId() != null) putNumber(root, submitted, "minDepositTermTypeId", f.getMinDepositTermTypeId());
|
||||
if (f.getMaxDepositTermTypeId() != null) putNumber(root, submitted, "maxDepositTermTypeId", f.getMaxDepositTermTypeId());
|
||||
if (f.getInMultiplesOfDepositTerm() != null) putNumber(root, submitted, "inMultiplesOfDepositTerm", f.getInMultiplesOfDepositTerm());
|
||||
if (f.getInMultiplesOfDepositTermTypeId() != null) putNumber(root, submitted, "inMultiplesOfDepositTermTypeId", f.getInMultiplesOfDepositTermTypeId());
|
||||
if (f.getMinDepositAmount() != null) putNumber(root, submitted, "minDepositAmount", f.getMinDepositAmount());
|
||||
if (f.getMaxDepositAmount() != null) putNumber(root, submitted, "maxDepositAmount", f.getMaxDepositAmount());
|
||||
if (f.getDepositAmount() != null) putNumber(root, submitted, "depositAmount", f.getDepositAmount());
|
||||
}
|
||||
|
||||
public static Set<String> populateRecurringDepositFields(final JsonCommand command, final RecurringDepositEditFields f) {
|
||||
final Set<String> present = new java.util.HashSet<>();
|
||||
if (command.hasParameter("isMandatoryDeposit")) {
|
||||
f.setIsMandatoryDeposit(command.booleanObjectValueOfParameterNamed("isMandatoryDeposit"));
|
||||
present.add("isMandatoryDeposit");
|
||||
}
|
||||
if (command.hasParameter("allowWithdrawal")) {
|
||||
f.setAllowWithdrawal(command.booleanObjectValueOfParameterNamed("allowWithdrawal"));
|
||||
present.add("allowWithdrawal");
|
||||
}
|
||||
if (command.hasParameter("adjustAdvanceTowardsFuturePayments")) {
|
||||
f.setAdjustAdvanceTowardsFuturePayments(command.booleanObjectValueOfParameterNamed("adjustAdvanceTowardsFuturePayments"));
|
||||
present.add("adjustAdvanceTowardsFuturePayments");
|
||||
}
|
||||
return present;
|
||||
}
|
||||
|
||||
public static void writeRecurringDepositFieldsToJson(final JsonObject root, final RecurringDepositEditFields f, final Set<String> submitted) {
|
||||
if (f == null) {
|
||||
return;
|
||||
}
|
||||
if (f.getIsMandatoryDeposit() != null) putBoolean(root, submitted, "isMandatoryDeposit", f.getIsMandatoryDeposit());
|
||||
if (f.getAllowWithdrawal() != null) putBoolean(root, submitted, "allowWithdrawal", f.getAllowWithdrawal());
|
||||
if (f.getAdjustAdvanceTowardsFuturePayments() != null) putBoolean(root, submitted, "adjustAdvanceTowardsFuturePayments", f.getAdjustAdvanceTowardsFuturePayments());
|
||||
}
|
||||
|
||||
public static java.util.Map<String, Object> reconstructProposedChangesMap(
|
||||
final ProductPendingEdit pendingEdit,
|
||||
final Set<String> submittedFields,
|
||||
final ProductPendingEditChargeRepository pendingEditChargeRepository,
|
||||
final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository,
|
||||
final ProductPendingEditFacilityLimitRepository pendingEditFacilityLimitRepository,
|
||||
final com.google.gson.Gson gson) {
|
||||
final JsonObject root = new JsonObject();
|
||||
root.addProperty("locale", "en");
|
||||
root.addProperty("dateFormat", "yyyy-MM-dd");
|
||||
|
||||
final String productType = pendingEdit.getProductType();
|
||||
if ((ProductPendingEdit.TYPE_SAVINGS.equals(productType) || ProductPendingEdit.TYPE_CURRENT.equals(productType) || ProductPendingEdit.TYPE_FD.equals(productType) || ProductPendingEdit.TYPE_RD.equals(productType)) && pendingEdit.getCoreFields() != null) {
|
||||
writeCoreFieldsToJson(root, pendingEdit.getCoreFields(), submittedFields);
|
||||
}
|
||||
if (ProductPendingEdit.TYPE_CURRENT.equals(productType) && pendingEdit.getCurrentOnlyFields() != null) {
|
||||
writeCurrentOnlyFieldsToJson(root, pendingEdit.getCurrentOnlyFields(), submittedFields);
|
||||
}
|
||||
if (ProductPendingEdit.TYPE_SAVINGS.equals(productType) || ProductPendingEdit.TYPE_CURRENT.equals(productType)) {
|
||||
if (submittedFields.contains("accountFacilityConfig") && pendingEdit.getFacilityConfigFields() != null) {
|
||||
writeFacilityConfigToJson(root, pendingEdit.getFacilityConfigFields(), true);
|
||||
}
|
||||
if (submittedFields.contains("depositLimitKyc") && pendingEdit.getDepositLimitKycFields() != null) {
|
||||
writeDepositLimitKycToJson(root, pendingEdit.getDepositLimitKycFields(), true);
|
||||
}
|
||||
if (submittedFields.contains("accountFacilityLimits") && pendingEditFacilityLimitRepository != null) {
|
||||
writeFacilityLimitsToJson(root, pendingEditFacilityLimitRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
}
|
||||
if ((ProductPendingEdit.TYPE_FD.equals(productType) || ProductPendingEdit.TYPE_RD.equals(productType)) && pendingEdit.getFixedDepositFields() != null) {
|
||||
writeFixedDepositFieldsToJson(root, pendingEdit.getFixedDepositFields(), submittedFields);
|
||||
}
|
||||
if (ProductPendingEdit.TYPE_RD.equals(productType) && pendingEdit.getRecurringDepositFields() != null) {
|
||||
writeRecurringDepositFieldsToJson(root, pendingEdit.getRecurringDepositFields(), submittedFields);
|
||||
}
|
||||
if (ProductPendingEdit.TYPE_SHARE.equals(productType) && pendingEdit.getShareOnlyFields() != null) {
|
||||
writeShareOnlyFieldsToJson(root, pendingEdit.getShareOnlyFields(), submittedFields);
|
||||
}
|
||||
if (submittedFields.contains("glAccountMapping") && pendingEdit.getGlAccountMappingFields() != null) {
|
||||
writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields());
|
||||
}
|
||||
if (submittedFields.contains("charges") && pendingEditChargeRepository != null) {
|
||||
writeChargesToJson(root, pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
if (submittedFields.contains("glArrayMappings") && pendingEditGlMappingRepository != null) {
|
||||
writeGlArrayMappingsToJson(root, pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId()));
|
||||
}
|
||||
|
||||
final String json = gson.toJson(root);
|
||||
return gson.fromJson(json, new com.google.gson.reflect.TypeToken<java.util.Map<String, Object>>() {}.getType());
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
|
||||
/**
|
||||
* One row per entry of the 3 array-shaped GL mappings (paymentChannelToFundSourceMappings,
|
||||
* feeToIncomeAccountMappings, penaltyToIncomeAccountMappings), distinguished by mappingType.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_product_temp_gl_mapping")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class ProductPendingEditGlMapping extends AbstractPersistableCustom<Long> {
|
||||
|
||||
public static final String TYPE_PAYMENT_CHANNEL = "PAYMENT_CHANNEL";
|
||||
public static final String TYPE_FEE = "FEE";
|
||||
public static final String TYPE_PENALTY = "PENALTY";
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "pending_edit_id", nullable = false)
|
||||
private ProductPendingEdit pendingEdit;
|
||||
|
||||
@Column(name = "mapping_type", nullable = false)
|
||||
private String mappingType;
|
||||
|
||||
@Column(name = "payment_type_id")
|
||||
private Long paymentTypeId;
|
||||
|
||||
@Column(name = "charge_id")
|
||||
private Long chargeId;
|
||||
|
||||
@Column(name = "gl_account_id", nullable = false)
|
||||
private Long glAccountId;
|
||||
|
||||
public ProductPendingEditGlMapping(final ProductPendingEdit pendingEdit, final String mappingType, final Long paymentTypeId,
|
||||
final Long chargeId, final Long glAccountId) {
|
||||
this.pendingEdit = pendingEdit;
|
||||
this.mappingType = mappingType;
|
||||
this.paymentTypeId = paymentTypeId;
|
||||
this.chargeId = chargeId;
|
||||
this.glAccountId = glAccountId;
|
||||
}
|
||||
}
|
||||
+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.savings.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface ProductPendingEditGlMappingRepository extends JpaRepository<ProductPendingEditGlMapping, Long> {
|
||||
|
||||
List<ProductPendingEditGlMapping> findByPendingEditId(Long pendingEditId);
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* 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.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
|
||||
/** One row per entry of Share Product's "marketPrice" array (create/update/delete-by-id semantics). */
|
||||
@Entity
|
||||
@Table(name = "m_product_temp_share_market_price")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class ProductPendingEditMarketPrice extends AbstractPersistableCustom<Long> {
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "pending_edit_id", nullable = false)
|
||||
private ProductPendingEdit pendingEdit;
|
||||
|
||||
/** id of the existing m_share_product_market_price row this entry updates/deletes, null if new. */
|
||||
@Column(name = "source_id")
|
||||
private Long sourceId;
|
||||
|
||||
@Column(name = "delete_flag", nullable = false)
|
||||
private boolean deleteFlag;
|
||||
|
||||
@Column(name = "from_date")
|
||||
private LocalDate fromDate;
|
||||
|
||||
@Column(name = "share_value")
|
||||
private BigDecimal shareValue;
|
||||
|
||||
public ProductPendingEditMarketPrice(final ProductPendingEdit pendingEdit, final Long sourceId, final boolean deleteFlag,
|
||||
final LocalDate fromDate, final BigDecimal shareValue) {
|
||||
this.pendingEdit = pendingEdit;
|
||||
this.sourceId = sourceId;
|
||||
this.deleteFlag = deleteFlag;
|
||||
this.fromDate = fromDate;
|
||||
this.shareValue = shareValue;
|
||||
}
|
||||
}
|
||||
+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.savings.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface ProductPendingEditMarketPriceRepository extends JpaRepository<ProductPendingEditMarketPrice, Long> {
|
||||
|
||||
List<ProductPendingEditMarketPrice> findByPendingEditId(Long pendingEditId);
|
||||
}
|
||||
+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.savings.domain;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface ProductPendingEditRepository extends JpaRepository<ProductPendingEdit, Long> {
|
||||
|
||||
Optional<ProductPendingEdit> findByProductTypeAndProductIdAndStatus(String productType, Long productId, String status);
|
||||
|
||||
List<ProductPendingEdit> findByProductTypeAndStatus(String productType, String status);
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
|
||||
/**
|
||||
* Records which top-level JSON parameter names were actually present in the maker's original
|
||||
* request. Typed nullable columns can't by themselves distinguish "field omitted" from "field
|
||||
* explicitly sent as null" from "field has a value" - and every existing command.hasParameter(...)
|
||||
* branch in the write-services and assemblers depends on that distinction - so this table is the
|
||||
* relational replacement for the information a raw JSON blob used to carry implicitly.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_product_temp_submitted_field")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class ProductPendingEditSubmittedField extends AbstractPersistableCustom<Long> {
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "pending_edit_id", nullable = false)
|
||||
private ProductPendingEdit pendingEdit;
|
||||
|
||||
@Column(name = "field_name", nullable = false)
|
||||
private String fieldName;
|
||||
|
||||
public ProductPendingEditSubmittedField(final ProductPendingEdit pendingEdit, final String fieldName) {
|
||||
this.pendingEdit = pendingEdit;
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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.savings.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface ProductPendingEditSubmittedFieldRepository extends JpaRepository<ProductPendingEditSubmittedField, Long> {
|
||||
|
||||
List<ProductPendingEditSubmittedField> findByPendingEditId(Long pendingEditId);
|
||||
|
||||
@Query("SELECT f.fieldName FROM ProductPendingEditSubmittedField f WHERE f.pendingEdit.id = :pendingEditId")
|
||||
List<String> findFieldNamesByPendingEditId(@Param("pendingEditId") Long pendingEditId);
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Embeddable
|
||||
@Getter
|
||||
@Setter
|
||||
public class RecurringDepositEditFields {
|
||||
|
||||
@Column(name = "is_mandatory_deposit")
|
||||
private Boolean isMandatoryDeposit;
|
||||
|
||||
@Column(name = "allow_withdrawal")
|
||||
private Boolean allowWithdrawal;
|
||||
|
||||
@Column(name = "adjust_advance_towards_future_payments")
|
||||
private Boolean adjustAdvanceTowardsFuturePayments;
|
||||
}
|
||||
+253
@@ -34,6 +34,8 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalA
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.overdraftLimitParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.introducerRequiredParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shareLinkingEnabledParamName;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import jakarta.persistence.CascadeType;
|
||||
@@ -1852,6 +1854,230 @@ public class SavingsAccount extends AbstractAuditableWithUTCDateTimeCustom<Long>
|
||||
baseDataValidator.reset().parameter(withHoldTaxParamName).failWithCode("not.supported.for.this.account");
|
||||
}
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed(introducerRequiredParamName, this.introducerRequired)) {
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(introducerRequiredParamName);
|
||||
actualChanges.put(introducerRequiredParamName, newValue);
|
||||
this.introducerRequired = newValue;
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed(shareLinkingEnabledParamName, this.shareLinkingEnabled)) {
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(shareLinkingEnabledParamName);
|
||||
actualChanges.put(shareLinkingEnabledParamName, newValue);
|
||||
this.shareLinkingEnabled = newValue;
|
||||
}
|
||||
|
||||
// CBS extended fields mapping during modify application
|
||||
final String accountTypeParam = "accountType";
|
||||
if (command.isChangeInStringParameterNamed(accountTypeParam, this.cbsAccountType)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(accountTypeParam);
|
||||
actualChanges.put(accountTypeParam, newValue);
|
||||
this.cbsAccountType = newValue;
|
||||
}
|
||||
|
||||
final String customerSegmentParam = "customerSegment";
|
||||
if (command.isChangeInStringParameterNamed(customerSegmentParam, this.customerSegment)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(customerSegmentParam);
|
||||
actualChanges.put(customerSegmentParam, newValue);
|
||||
this.customerSegment = newValue;
|
||||
}
|
||||
|
||||
final String accountStatusParam = "accountStatus";
|
||||
if (command.isChangeInStringParameterNamed(accountStatusParam, this.accountStatus)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(accountStatusParam);
|
||||
actualChanges.put(accountStatusParam, newValue);
|
||||
this.accountStatus = newValue;
|
||||
}
|
||||
|
||||
final String modeOfOperationParam = "modeOfOperation";
|
||||
if (command.isChangeInStringParameterNamed(modeOfOperationParam, this.modeOfOperation)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(modeOfOperationParam);
|
||||
actualChanges.put(modeOfOperationParam, newValue);
|
||||
this.modeOfOperation = newValue;
|
||||
}
|
||||
|
||||
final String specialCategoryParam = "specialCategory";
|
||||
if (command.isChangeInStringParameterNamed(specialCategoryParam, this.specialCategory)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(specialCategoryParam);
|
||||
actualChanges.put(specialCategoryParam, newValue);
|
||||
this.specialCategory = newValue;
|
||||
}
|
||||
|
||||
final String weakerSectionParam = "weakerSection";
|
||||
if (command.isChangeInStringParameterNamed(weakerSectionParam, this.weakerSection)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(weakerSectionParam);
|
||||
actualChanges.put(weakerSectionParam, newValue);
|
||||
this.weakerSection = newValue;
|
||||
}
|
||||
|
||||
final String nominationFacilityParam = "nominationFacility";
|
||||
if (command.isChangeInBooleanParameterNamed(nominationFacilityParam, this.nominationFacility)) {
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(nominationFacilityParam);
|
||||
actualChanges.put(nominationFacilityParam, newValue);
|
||||
this.nominationFacility = newValue;
|
||||
}
|
||||
|
||||
final String autoDebitFacilityParam = "autoDebitFacility";
|
||||
if (command.isChangeInBooleanParameterNamed(autoDebitFacilityParam, this.autoDebitFacility)) {
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(autoDebitFacilityParam);
|
||||
actualChanges.put(autoDebitFacilityParam, newValue);
|
||||
this.autoDebitFacility = newValue;
|
||||
}
|
||||
|
||||
final String isSeniorCitizenParam = "isSeniorCitizen";
|
||||
if (command.isChangeInBooleanParameterNamed(isSeniorCitizenParam, this.isSeniorCitizen)) {
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(isSeniorCitizenParam);
|
||||
actualChanges.put(isSeniorCitizenParam, newValue);
|
||||
this.isSeniorCitizen = newValue;
|
||||
}
|
||||
|
||||
final String seniorCitizenInterestRateParam = "seniorCitizenInterestRate";
|
||||
if (command.isChangeInBigDecimalParameterNamed(seniorCitizenInterestRateParam, this.seniorCitizenInterestRate)) {
|
||||
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(seniorCitizenInterestRateParam);
|
||||
actualChanges.put(seniorCitizenInterestRateParam, newValue);
|
||||
this.seniorCitizenInterestRate = newValue;
|
||||
if (newValue != null && newValue.compareTo(BigDecimal.ZERO) > 0) {
|
||||
updateNominalAnnualInterestRate(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
final String guardianNameParam = "guardianName";
|
||||
if (command.isChangeInStringParameterNamed(guardianNameParam, this.guardianName)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(guardianNameParam);
|
||||
actualChanges.put(guardianNameParam, newValue);
|
||||
this.guardianName = newValue;
|
||||
}
|
||||
|
||||
final String guardianCifNumberParam = "guardianCifNumber";
|
||||
if (command.isChangeInStringParameterNamed(guardianCifNumberParam, this.guardianCifNumber)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(guardianCifNumberParam);
|
||||
actualChanges.put(guardianCifNumberParam, newValue);
|
||||
this.guardianCifNumber = newValue;
|
||||
}
|
||||
|
||||
final String guardianRelationshipParam = "guardianRelationship";
|
||||
if (command.isChangeInStringParameterNamed(guardianRelationshipParam, this.guardianRelationship)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(guardianRelationshipParam);
|
||||
actualChanges.put(guardianRelationshipParam, newValue);
|
||||
this.guardianRelationship = newValue;
|
||||
}
|
||||
|
||||
final String minorDateOfBirthParam = "minorDateOfBirth";
|
||||
if (command.isChangeInLocalDateParameterNamed(minorDateOfBirthParam, this.minorDateOfBirth)) {
|
||||
final LocalDate newValue = command.localDateValueOfParameterNamed(minorDateOfBirthParam);
|
||||
actualChanges.put(minorDateOfBirthParam, newValue);
|
||||
this.minorDateOfBirth = newValue;
|
||||
}
|
||||
|
||||
final String guardianClientIdParam = "guardianClientId";
|
||||
if (command.isChangeInLongParameterNamed(guardianClientIdParam, this.guardianClientId)) {
|
||||
final Long newValue = command.longValueOfParameterNamed(guardianClientIdParam);
|
||||
actualChanges.put(guardianClientIdParam, newValue);
|
||||
this.guardianClientId = newValue;
|
||||
}
|
||||
|
||||
final String introducerIdParam = "introducerId";
|
||||
if (command.isChangeInLongParameterNamed(introducerIdParam, this.introducerClientId)) {
|
||||
final Long newValue = command.longValueOfParameterNamed(introducerIdParam);
|
||||
actualChanges.put(introducerIdParam, newValue);
|
||||
this.introducerClientId = newValue;
|
||||
}
|
||||
|
||||
final String introducerNameParam = "introducerName";
|
||||
if (command.isChangeInStringParameterNamed(introducerNameParam, this.introducerName)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(introducerNameParam);
|
||||
actualChanges.put(introducerNameParam, newValue);
|
||||
this.introducerName = newValue;
|
||||
}
|
||||
|
||||
final String introducerCifNumberParam = "introducerCifNumber";
|
||||
if (command.isChangeInStringParameterNamed(introducerCifNumberParam, this.introducerCifNumber)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(introducerCifNumberParam);
|
||||
actualChanges.put(introducerCifNumberParam, newValue);
|
||||
this.introducerCifNumber = newValue;
|
||||
}
|
||||
|
||||
final String linkedAccountIdParam = "linkedAccountId";
|
||||
if (command.isChangeInLongParameterNamed(linkedAccountIdParam, this.linkedAccountId)) {
|
||||
final Long newValue = command.longValueOfParameterNamed(linkedAccountIdParam);
|
||||
actualChanges.put(linkedAccountIdParam, newValue);
|
||||
this.linkedAccountId = newValue;
|
||||
}
|
||||
|
||||
final String linkedAccountTypeParam = "linkedAccountType";
|
||||
if (command.isChangeInStringParameterNamed(linkedAccountTypeParam, this.linkedAccountType)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(linkedAccountTypeParam);
|
||||
actualChanges.put(linkedAccountTypeParam, newValue);
|
||||
this.linkedAccountType = newValue;
|
||||
}
|
||||
|
||||
final String linkedAccountNoParam = "linkedAccountNo";
|
||||
if (command.isChangeInStringParameterNamed(linkedAccountNoParam, this.linkedAccountNo)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(linkedAccountNoParam);
|
||||
actualChanges.put(linkedAccountNoParam, newValue);
|
||||
this.linkedAccountNo = newValue;
|
||||
}
|
||||
|
||||
final String accountFacilityConfigParam = "accountFacilityConfig";
|
||||
if (command.hasParameter(accountFacilityConfigParam)) {
|
||||
final com.google.gson.JsonElement parsedJson = command.parsedJson();
|
||||
if (parsedJson != null && parsedJson.isJsonObject()) {
|
||||
final com.google.gson.JsonObject jsonObj = parsedJson.getAsJsonObject();
|
||||
if (jsonObj.has(accountFacilityConfigParam) && !jsonObj.get(accountFacilityConfigParam).isJsonNull()
|
||||
&& jsonObj.get(accountFacilityConfigParam).isJsonObject()) {
|
||||
final com.google.gson.JsonObject configObj = jsonObj.get(accountFacilityConfigParam).getAsJsonObject();
|
||||
if (this.facilityConfig == null) {
|
||||
this.facilityConfig = new SavingsAccountFacilityConfig(this);
|
||||
}
|
||||
this.facilityConfig.updateFrom(configObj);
|
||||
actualChanges.put(accountFacilityConfigParam, configObj.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final String jointHoldersParam = "jointHolders";
|
||||
if (command.hasParameter(jointHoldersParam)) {
|
||||
final com.google.gson.JsonElement parsedJson = command.parsedJson();
|
||||
if (parsedJson != null && parsedJson.isJsonObject()) {
|
||||
final com.google.gson.JsonObject jsonObj = parsedJson.getAsJsonObject();
|
||||
if (jsonObj.has(jointHoldersParam) && !jsonObj.get(jointHoldersParam).isJsonNull()
|
||||
&& jsonObj.get(jointHoldersParam).isJsonArray()) {
|
||||
final com.google.gson.JsonArray holdersArray = jsonObj.get(jointHoldersParam).getAsJsonArray();
|
||||
final List<SavingsAccountJointHolder> holders = new ArrayList<>();
|
||||
for (int i = 0; i < holdersArray.size(); i++) {
|
||||
final com.google.gson.JsonObject h = holdersArray.get(i).getAsJsonObject();
|
||||
final Long clientId = h.has("clientId") && !h.get("clientId").isJsonNull()
|
||||
? h.get("clientId").getAsLong() : null;
|
||||
final String cifNumber = h.has("cifNumber") && !h.get("cifNumber").isJsonNull()
|
||||
? h.get("cifNumber").getAsString() : null;
|
||||
final String displayName = h.has("displayName") && !h.get("displayName").isJsonNull()
|
||||
? h.get("displayName").getAsString() : null;
|
||||
final BigDecimal sharePercentage = h.has("sharePercentage") && !h.get("sharePercentage").isJsonNull()
|
||||
? h.get("sharePercentage").getAsBigDecimal() : null;
|
||||
final boolean isPrimary = h.has("isPrimary") && h.get("isPrimary").getAsBoolean();
|
||||
String signingAuthority = h.has("signingAuthority") && !h.get("signingAuthority").isJsonNull()
|
||||
? h.get("signingAuthority").getAsString() : null;
|
||||
String role = h.has("role") && !h.get("role").isJsonNull()
|
||||
? h.get("role").getAsString() : null;
|
||||
|
||||
if (signingAuthority == null || signingAuthority.trim().isEmpty()) {
|
||||
signingAuthority = isPrimary ? "Mandatory" : "Optional";
|
||||
}
|
||||
if (role == null || role.trim().isEmpty()) {
|
||||
role = isPrimary ? "Primary Account Holder" : "Co-Holder";
|
||||
}
|
||||
|
||||
final boolean isMandatory = h.has("isMandatory") && h.get("isMandatory").getAsBoolean();
|
||||
holders.add(new SavingsAccountJointHolder(this, clientId, cifNumber, displayName,
|
||||
sharePercentage, signingAuthority, role, isPrimary, isMandatory));
|
||||
}
|
||||
this.jointHolders.clear();
|
||||
this.jointHolders.addAll(holders);
|
||||
actualChanges.put(jointHoldersParam, holdersArray.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
validateLockinDetails(baseDataValidator);
|
||||
esnureOverdraftLimitsSetForOverdraftAccounts();
|
||||
}
|
||||
@@ -3972,6 +4198,12 @@ public class SavingsAccount extends AbstractAuditableWithUTCDateTimeCustom<Long>
|
||||
@Column(name = "linked_account_no", length = 50)
|
||||
private String linkedAccountNo;
|
||||
|
||||
@Column(name = "introducer_required", nullable = false, columnDefinition = "boolean default false")
|
||||
private Boolean introducerRequired = false;
|
||||
|
||||
@Column(name = "share_linking_enabled", nullable = false, columnDefinition = "boolean default false")
|
||||
private Boolean shareLinkingEnabled = false;
|
||||
|
||||
@Column(name = "nomination_facility", nullable = false, columnDefinition = "boolean default false")
|
||||
private Boolean nominationFacility = false;
|
||||
|
||||
@@ -3984,6 +4216,21 @@ public class SavingsAccount extends AbstractAuditableWithUTCDateTimeCustom<Long>
|
||||
@Column(name = "senior_citizen_interest_rate", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal seniorCitizenInterestRate;
|
||||
|
||||
@Column(name = "terminate_true", nullable = false, columnDefinition = "boolean default false")
|
||||
private boolean terminateTrue = false;
|
||||
|
||||
public boolean isTerminateTrue() { return terminateTrue; }
|
||||
public void setTerminateTrue(boolean terminateTrue) { this.terminateTrue = terminateTrue; }
|
||||
|
||||
public Map<String, Object> terminate() {
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<>();
|
||||
this.terminateTrue = true;
|
||||
this.status = SavingsAccountStatusType.TERMINATED.getValue();
|
||||
actualChanges.put("terminateTrue", true);
|
||||
actualChanges.put("status", SavingsAccountStatusType.TERMINATED.getValue());
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
|
||||
@jakarta.persistence.OneToOne(cascade = jakarta.persistence.CascadeType.ALL, mappedBy = "savingsAccount", orphanRemoval = true, fetch = FetchType.LAZY)
|
||||
private SavingsAccountFacilityConfig facilityConfig;
|
||||
@@ -4057,6 +4304,12 @@ public class SavingsAccount extends AbstractAuditableWithUTCDateTimeCustom<Long>
|
||||
public Boolean getAutoDebitFacility() { return autoDebitFacility; }
|
||||
public void setAutoDebitFacility(Boolean v) { this.autoDebitFacility = v; }
|
||||
|
||||
public Boolean getIntroducerRequired() { return introducerRequired; }
|
||||
public void setIntroducerRequired(Boolean v) { this.introducerRequired = v; }
|
||||
|
||||
public Boolean getShareLinkingEnabled() { return shareLinkingEnabled; }
|
||||
public void setShareLinkingEnabled(Boolean v) { this.shareLinkingEnabled = v; }
|
||||
|
||||
public boolean isSeniorCitizen() { return isSeniorCitizen; }
|
||||
public void setSeniorCitizen(boolean v) { this.isSeniorCitizen = v; }
|
||||
|
||||
|
||||
+8
-4
@@ -815,16 +815,20 @@ public class SavingsAccountCharge extends AbstractAuditableWithUTCDateTimeCustom
|
||||
return nextDueLocalDate;
|
||||
}
|
||||
|
||||
public Integer getFeeInterval() {
|
||||
return this.feeInterval == null ? 1 : this.feeInterval;
|
||||
}
|
||||
|
||||
private LocalDate calculateNextDueDate(final LocalDate date) {
|
||||
LocalDate nextDueLocalDate = null;
|
||||
if (isAnnualFee()) {
|
||||
nextDueLocalDate = date.withMonth(this.feeOnMonth).plusYears(1);
|
||||
nextDueLocalDate = setDayOfMonth(nextDueLocalDate);
|
||||
} else if (isMonthlyFee()) {
|
||||
nextDueLocalDate = date.plusMonths(this.feeInterval);
|
||||
nextDueLocalDate = date.plusMonths(getFeeInterval());
|
||||
nextDueLocalDate = setDayOfMonth(nextDueLocalDate);
|
||||
} else if (isWeeklyFee()) {
|
||||
nextDueLocalDate = date.plusWeeks(this.feeInterval);
|
||||
nextDueLocalDate = date.plusWeeks(getFeeInterval());
|
||||
nextDueLocalDate = setDayOfWeek(nextDueLocalDate);
|
||||
}
|
||||
return nextDueLocalDate;
|
||||
@@ -857,10 +861,10 @@ public class SavingsAccountCharge extends AbstractAuditableWithUTCDateTimeCustom
|
||||
nextDueLocalDate = nextDueLocalDate.withMonth(this.feeOnMonth).minusYears(1);
|
||||
nextDueLocalDate = setDayOfMonth(nextDueLocalDate);
|
||||
} else if (isMonthlyFee()) {
|
||||
nextDueLocalDate = nextDueLocalDate.minusMonths(this.feeInterval);
|
||||
nextDueLocalDate = nextDueLocalDate.minusMonths(getFeeInterval());
|
||||
nextDueLocalDate = setDayOfMonth(nextDueLocalDate);
|
||||
} else if (isWeeklyFee()) {
|
||||
nextDueLocalDate = nextDueLocalDate.minusDays(7 * this.feeInterval);
|
||||
nextDueLocalDate = nextDueLocalDate.minusDays(7 * getFeeInterval());
|
||||
nextDueLocalDate = setDayOfWeek(nextDueLocalDate);
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user