Fix inconsistenant Date and Date time handling

This commit is contained in:
Adam Saghy
2022-08-01 15:47:47 +02:00
committed by Arnold Gálovics
parent 7fb61d028e
commit 65c6b18ed1
20 changed files with 595 additions and 364 deletions
@@ -46,9 +46,9 @@ public class AccrualAccountingWritePlatformServiceImpl implements AccrualAccount
@Override @Override
public CommandProcessingResult executeLoansPeriodicAccrual(JsonCommand command) { public CommandProcessingResult executeLoansPeriodicAccrual(JsonCommand command) {
this.accountingDataValidator.validateLoanPeriodicAccrualData(command.json()); this.accountingDataValidator.validateLoanPeriodicAccrualData(command.json());
LocalDate tilldate = command.localDateValueOfParameterNamed(accrueTillParamName); LocalDate tillDate = command.localDateValueOfParameterNamed(accrueTillParamName);
try { try {
this.loanAccrualPlatformService.addPeriodicAccruals(tilldate); this.loanAccrualPlatformService.addPeriodicAccruals(tillDate);
} catch (MultiException e) { } catch (MultiException e) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
@@ -26,6 +26,8 @@ import java.sql.Timestamp;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.LocalTime; import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.springframework.jdbc.support.JdbcUtils; import org.springframework.jdbc.support.JdbcUtils;
@@ -133,4 +135,14 @@ public final class JdbcSupport {
} }
return result; return result;
} }
public static OffsetDateTime getOffsetDateTime(ResultSet rs, String columnName) throws SQLException {
final Timestamp timestamp = rs.getTimestamp(columnName);
if (timestamp != null) {
OffsetDateTime offsetDateTimeAtUTC = OffsetDateTime.of(timestamp.toLocalDateTime(), ZoneOffset.UTC);
return offsetDateTimeAtUTC
.withOffsetSameInstant(DateUtils.getDateTimeZoneOfTenant().getRules().getOffset(offsetDateTimeAtUTC.toInstant()));
}
return null;
}
} }
@@ -22,6 +22,7 @@ import org.apache.fineract.infrastructure.core.service.RoutingDataSourceServiceF
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.apache.fineract.infrastructure.core.service.database.DatabaseTypeResolver; import org.apache.fineract.infrastructure.core.service.database.DatabaseTypeResolver;
import org.apache.fineract.infrastructure.jobs.service.JobName; import org.apache.fineract.infrastructure.jobs.service.JobName;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.springframework.batch.core.Job; import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step; import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
@@ -46,6 +47,9 @@ public class UpdateNpaConfig {
@Autowired @Autowired
private DatabaseSpecificSQLGenerator sqlGenerator; private DatabaseSpecificSQLGenerator sqlGenerator;
@Autowired
private PlatformSecurityContext platformSecurityContext;
@Bean @Bean
protected Step updateNpaStep() { protected Step updateNpaStep() {
return steps.get(JobName.UPDATE_NPA.name()).tasklet(updateNpaTasklet()).build(); return steps.get(JobName.UPDATE_NPA.name()).tasklet(updateNpaTasklet()).build();
@@ -58,6 +62,6 @@ public class UpdateNpaConfig {
@Bean @Bean
public UpdateNpaTasklet updateNpaTasklet() { public UpdateNpaTasklet updateNpaTasklet() {
return new UpdateNpaTasklet(dataSourceServiceFactory, databaseTypeResolver, sqlGenerator); return new UpdateNpaTasklet(dataSourceServiceFactory, databaseTypeResolver, sqlGenerator, platformSecurityContext);
} }
} }
@@ -20,10 +20,13 @@ package org.apache.fineract.infrastructure.jobs.service.updatenpa;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.RoutingDataSourceServiceFactory; import org.apache.fineract.infrastructure.core.service.RoutingDataSourceServiceFactory;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.apache.fineract.infrastructure.core.service.database.DatabaseTypeResolver; import org.apache.fineract.infrastructure.core.service.database.DatabaseTypeResolver;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.useradministration.domain.AppUser;
import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.core.step.tasklet.Tasklet;
@@ -37,9 +40,11 @@ public class UpdateNpaTasklet implements Tasklet {
private final RoutingDataSourceServiceFactory dataSourceServiceFactory; private final RoutingDataSourceServiceFactory dataSourceServiceFactory;
private final DatabaseTypeResolver databaseTypeResolver; private final DatabaseTypeResolver databaseTypeResolver;
private final DatabaseSpecificSQLGenerator sqlGenerator; private final DatabaseSpecificSQLGenerator sqlGenerator;
private final PlatformSecurityContext context;
@Override @Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
AppUser user = context.getAuthenticatedUserIfPresent();
final JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceServiceFactory.determineDataSourceService().retrieveDataSource()); final JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceServiceFactory.determineDataSourceService().retrieveDataSource());
final StringBuilder resetNPASqlBuilder = new StringBuilder(); final StringBuilder resetNPASqlBuilder = new StringBuilder();
@@ -52,11 +57,13 @@ public class UpdateNpaTasklet implements Tasklet {
String wherePart = " where loan.id = sl.id "; String wherePart = " where loan.id = sl.id ";
if (databaseTypeResolver.isMySQL()) { if (databaseTypeResolver.isMySQL()) {
resetNPASqlBuilder.append(", ").append(fromPart).append(" set loan.is_npa = false").append(wherePart); resetNPASqlBuilder.append(", ").append(fromPart).append(" set loan.is_npa = false")
.append(", loan.last_modified_by = ?, loan.last_modified_on_utc = ? ").append(wherePart);
} else { } else {
resetNPASqlBuilder.append("set is_npa = false").append(" FROM ").append(fromPart).append(wherePart); resetNPASqlBuilder.append("set is_npa = false").append(", last_modified_by = ?, last_modified_on_utc = ? ").append(" FROM ")
.append(fromPart).append(wherePart);
} }
jdbcTemplate.update(resetNPASqlBuilder.toString()); jdbcTemplate.update(resetNPASqlBuilder.toString(), user.getId(), DateUtils.getOffsetDateTimeOfTenant());
final StringBuilder updateSqlBuilder = new StringBuilder(900); final StringBuilder updateSqlBuilder = new StringBuilder(900);
@@ -68,12 +75,14 @@ public class UpdateNpaTasklet implements Tasklet {
wherePart = " where ml.id=sl.id "; wherePart = " where ml.id=sl.id ";
updateSqlBuilder.append("UPDATE m_loan as ml "); updateSqlBuilder.append("UPDATE m_loan as ml ");
if (databaseTypeResolver.isMySQL()) { if (databaseTypeResolver.isMySQL()) {
updateSqlBuilder.append(", ").append(fromPart).append(" SET ml.is_npa = true").append(wherePart); updateSqlBuilder.append(", ").append(fromPart).append(" SET ml.is_npa = true")
.append(", ml.last_modified_by = ?, ml.last_modified_on_utc = ? ").append(wherePart);
} else { } else {
updateSqlBuilder.append(" SET is_npa = true").append(" FROM ").append(fromPart).append(wherePart); updateSqlBuilder.append(" SET is_npa = true").append(", last_modified_by = ?, last_modified_on_utc = ? ").append(" FROM ")
.append(fromPart).append(wherePart);
} }
final int result = jdbcTemplate.update(updateSqlBuilder.toString()); final int result = jdbcTemplate.update(updateSqlBuilder.toString(), user.getId(), DateUtils.getOffsetDateTimeOfTenant());
log.info("{}: Records affected by updateNPA: {}", ThreadLocalContextUtil.getTenant().getName(), result); log.info("{}: Records affected by updateNPA: {}", ThreadLocalContextUtil.getTenant().getName(), result);
return RepeatStatus.FINISHED; return RepeatStatus.FINISHED;
@@ -20,7 +20,6 @@ package org.apache.fineract.portfolio.client.domain;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@@ -39,18 +38,17 @@ import javax.persistence.Transient;
import javax.persistence.UniqueConstraint; import javax.persistence.UniqueConstraint;
import org.apache.fineract.accounting.glaccount.domain.GLAccount; import org.apache.fineract.accounting.glaccount.domain.GLAccount;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.organisation.office.domain.Office; import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.organisation.office.domain.OrganisationCurrency; import org.apache.fineract.organisation.office.domain.OrganisationCurrency;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail; import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
import org.apache.fineract.useradministration.domain.AppUser;
@Entity @Entity
@Table(name = "m_client_transaction", uniqueConstraints = { @UniqueConstraint(columnNames = { "external_id" }, name = "external_id") }) @Table(name = "m_client_transaction", uniqueConstraints = { @UniqueConstraint(columnNames = { "external_id" }, name = "external_id") })
public class ClientTransaction extends AbstractPersistableCustom { public class ClientTransaction extends AbstractAuditableWithUTCDateTimeCustom {
@ManyToOne(optional = false) @ManyToOne(optional = false)
@JoinColumn(name = "client_id", nullable = false) @JoinColumn(name = "client_id", nullable = false)
@@ -73,6 +71,9 @@ public class ClientTransaction extends AbstractPersistableCustom {
@Column(name = "transaction_date", nullable = false) @Column(name = "transaction_date", nullable = false)
private LocalDate dateOf; private LocalDate dateOf;
@Column(name = "submitted_on_date", nullable = false)
private LocalDate submittedOnDate;
@Column(name = "amount", scale = 6, precision = 19, nullable = false) @Column(name = "amount", scale = 6, precision = 19, nullable = false)
private BigDecimal amount; private BigDecimal amount;
@@ -82,12 +83,11 @@ public class ClientTransaction extends AbstractPersistableCustom {
@Column(name = "external_id", length = 100, nullable = true, unique = true) @Column(name = "external_id", length = 100, nullable = true, unique = true)
private String externalId; private String externalId;
@Column(name = "created_date", nullable = false) /*
private LocalDateTime createdDate; * Deprecated since common Auditable fields were introduced. Columns and data left untouched to help migration.
*
@ManyToOne * @Column(name = "created_date", nullable = false) private LocalDateTime createdDate;
@JoinColumn(name = "appuser_id", nullable = true) */
private AppUser appUser;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "clientTransaction", orphanRemoval = true, fetch = FetchType.EAGER) @OneToMany(cascade = CascadeType.ALL, mappedBy = "clientTransaction", orphanRemoval = true, fetch = FetchType.EAGER)
private Set<ClientChargePaidBy> clientChargePaidByCollection = new HashSet<>(); private Set<ClientChargePaidBy> clientChargePaidByCollection = new HashSet<>();
@@ -97,37 +97,36 @@ public class ClientTransaction extends AbstractPersistableCustom {
protected ClientTransaction() {} protected ClientTransaction() {}
public static ClientTransaction payCharge(final Client client, final Office office, PaymentDetail paymentDetail, final LocalDate date, public static ClientTransaction payCharge(final Client client, final Office office, PaymentDetail paymentDetail,
final Money amount, final String currencyCode, final AppUser appUser) { final LocalDate transactionDate, final Money amount, final String currencyCode) {
final boolean isReversed = false; final boolean isReversed = false;
final String externalId = null; final String externalId = null;
return new ClientTransaction(client, office, paymentDetail, ClientTransactionType.PAY_CHARGE.getValue(), date, amount, isReversed, return new ClientTransaction(client, office, paymentDetail, ClientTransactionType.PAY_CHARGE.getValue(), transactionDate, amount,
externalId, DateUtils.getLocalDateTimeOfTenant(), currencyCode, appUser); isReversed, externalId, currencyCode);
} }
public static ClientTransaction waiver(final Client client, final Office office, final LocalDate date, final Money amount, public static ClientTransaction waiver(final Client client, final Office office, final LocalDate transactionDate, final Money amount,
final String currencyCode, final AppUser appUser) { final String currencyCode) {
final boolean isReversed = false; final boolean isReversed = false;
final String externalId = null; final String externalId = null;
final PaymentDetail paymentDetail = null; final PaymentDetail paymentDetail = null;
return new ClientTransaction(client, office, paymentDetail, ClientTransactionType.WAIVE_CHARGE.getValue(), date, amount, isReversed, return new ClientTransaction(client, office, paymentDetail, ClientTransactionType.WAIVE_CHARGE.getValue(), transactionDate, amount,
externalId, DateUtils.getLocalDateTimeOfTenant(), currencyCode, appUser); isReversed, externalId, currencyCode);
} }
public ClientTransaction(Client client, Office office, PaymentDetail paymentDetail, Integer typeOf, LocalDate transactionLocalDate, public ClientTransaction(Client client, Office office, PaymentDetail paymentDetail, Integer typeOf, LocalDate transactionDate,
Money amount, boolean reversed, String externalId, LocalDateTime createdDate, String currencyCode, AppUser appUser) { Money amount, boolean reversed, String externalId, String currencyCode) {
this.client = client; this.client = client;
this.office = office; this.office = office;
this.paymentDetail = paymentDetail; this.paymentDetail = paymentDetail;
this.typeOf = typeOf; this.typeOf = typeOf;
this.dateOf = transactionLocalDate; this.dateOf = transactionDate;
this.amount = amount.getAmount(); this.amount = amount.getAmount();
this.reversed = reversed; this.reversed = reversed;
this.externalId = externalId; this.externalId = externalId;
this.createdDate = createdDate;
this.currencyCode = currencyCode; this.currencyCode = currencyCode;
this.appUser = appUser; this.submittedOnDate = DateUtils.getBusinessLocalDate();
} }
public void reverse() { public void reverse() {
@@ -226,4 +225,8 @@ public class ClientTransaction extends AbstractPersistableCustom {
return this.dateOf; return this.dateOf;
} }
public LocalDate getSubmittedOnDate() {
return this.submittedOnDate;
}
} }
@@ -54,7 +54,6 @@ import org.apache.fineract.portfolio.client.domain.ClientTransaction;
import org.apache.fineract.portfolio.client.domain.ClientTransactionRepository; import org.apache.fineract.portfolio.client.domain.ClientTransactionRepository;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail; import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
import org.apache.fineract.portfolio.paymentdetail.service.PaymentDetailWritePlatformService; import org.apache.fineract.portfolio.paymentdetail.service.PaymentDetailWritePlatformService;
import org.apache.fineract.useradministration.domain.AppUser;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -174,7 +173,7 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
ClientTransaction clientTransaction = ClientTransaction.payCharge(client, client.getOffice(), paymentDetail, transactionDate, ClientTransaction clientTransaction = ClientTransaction.payCharge(client, client.getOffice(), paymentDetail, transactionDate,
chargePaid, clientCharge.getCurrency().getCode(), getAppUserIfPresent()); chargePaid, clientCharge.getCurrency().getCode());
this.clientTransactionRepository.saveAndFlush(clientTransaction); this.clientTransactionRepository.saveAndFlush(clientTransaction);
// update charge paid by associations // update charge paid by associations
@@ -217,7 +216,7 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
// create Waiver Transaction // create Waiver Transaction
ClientTransaction clientTransaction = ClientTransaction.waiver(client, client.getOffice(), transactionDate, waivedAmount, ClientTransaction clientTransaction = ClientTransaction.waiver(client, client.getOffice(), transactionDate, waivedAmount,
clientCharge.getCurrency().getCode(), getAppUserIfPresent()); clientCharge.getCurrency().getCode());
this.clientTransactionRepository.saveAndFlush(clientTransaction); this.clientTransactionRepository.saveAndFlush(clientTransaction);
// update charge paid by associations // update charge paid by associations
@@ -392,7 +391,8 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
/** /**
* Ensures that the charge transaction date (for payments) is not on a holiday or a non working day * Ensures that the charge transaction date (for payments) is not on a holiday or a non working day
* *
* @param savingsAccountCharge * @param transactionDate
* @param clientCharge
* @param fmt * @param fmt
*/ */
private void validateTransactionDateOnWorkingDay(final LocalDate transactionDate, final ClientCharge clientCharge, private void validateTransactionDateOnWorkingDay(final LocalDate transactionDate, final ClientCharge clientCharge,
@@ -406,7 +406,8 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
* @param date * @param date
* @param officeId * @param officeId
* @param jsonPropertyName * @param jsonPropertyName
* @param errorMessageFragment * @param errorMessageFragmentForActivityOnHoliday
* @param errorMessageFragmentForActivityOnNonWorkingDay
* @param fmt * @param fmt
*/ */
private void validateActivityDateFallOnAWorkingDay(final LocalDate date, final Long officeId, final String jsonPropertyName, private void validateActivityDateFallOnAWorkingDay(final LocalDate date, final Long officeId, final String jsonPropertyName,
@@ -436,14 +437,6 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
} }
} }
private AppUser getAppUserIfPresent() {
AppUser user = null;
if (this.context != null) {
user = this.context.getAuthenticatedUserIfPresent();
}
return user;
}
private void handleDataIntegrityIssues(@SuppressWarnings("unused") final Long clientId, final Long clientChargeId, private void handleDataIntegrityIssues(@SuppressWarnings("unused") final Long clientId, final Long clientChargeId,
final Throwable realCause, final NonTransientDataAccessException dve) { final Throwable realCause, final NonTransientDataAccessException dve) {
@@ -68,7 +68,7 @@ public class ClientTransactionReadPlatformServiceImpl implements ClientTransacti
final StringBuilder sqlBuilder = new StringBuilder(400); final StringBuilder sqlBuilder = new StringBuilder(400);
sqlBuilder.append("tr.id as transactionId, tr.transaction_type_enum as transactionType, "); sqlBuilder.append("tr.id as transactionId, tr.transaction_type_enum as transactionType, ");
sqlBuilder.append("tr.transaction_date as transactionDate, tr.amount as transactionAmount, "); sqlBuilder.append("tr.transaction_date as transactionDate, tr.amount as transactionAmount, ");
sqlBuilder.append("tr.created_date as submittedOnDate, tr.is_reversed as reversed, "); sqlBuilder.append("tr.submitted_on_date as submittedOnDate, tr.is_reversed as reversed, ");
sqlBuilder.append("tr.external_id as externalId, o.name as officeName, o.id as officeId, "); sqlBuilder.append("tr.external_id as externalId, o.name as officeName, o.id as officeId, ");
sqlBuilder.append("c.id as clientId, c.account_no as accountNo, ccpb.client_charge_id as clientChargeId, "); sqlBuilder.append("c.id as clientId, c.account_no as accountNo, ccpb.client_charge_id as clientChargeId, ");
sqlBuilder.append("pd.payment_type_id as paymentType,pd.account_number as accountNumber,pd.check_number as checkNumber, "); sqlBuilder.append("pd.payment_type_id as paymentType,pd.account_number as accountNumber,pd.check_number as checkNumber, ");
@@ -143,7 +143,7 @@ public class ClientTransactionReadPlatformServiceImpl implements ClientTransacti
sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " ").append(this.clientTransactionMapper.schema()) sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " ").append(this.clientTransactionMapper.schema())
.append(" where c.id = ? "); .append(" where c.id = ? ");
parameters[0] = clientId; parameters[0] = clientId;
sqlBuilder.append(" order by tr.transaction_date DESC, tr.created_date DESC, tr.id DESC "); sqlBuilder.append(" order by tr.transaction_date DESC, tr.submitted_on_date DESC, tr.id DESC ");
// apply limit and offsets // apply limit and offsets
@@ -169,7 +169,7 @@ public class ClientTransactionReadPlatformServiceImpl implements ClientTransacti
sql = sql + " and ccpb.client_charge_id = ?"; sql = sql + " and ccpb.client_charge_id = ?";
} }
parameters[0] = clientId; parameters[0] = clientId;
sql = sql + " order by tr.transaction_date DESC, tr.created_date DESC, tr.id DESC"; sql = sql + " order by tr.transaction_date DESC, tr.submitted_on_date DESC, tr.id DESC";
return this.jdbcTemplate.query(sql, this.clientTransactionMapper, parameters); // NOSONAR return this.jdbcTemplate.query(sql, this.clientTransactionMapper, parameters); // NOSONAR
} }
@@ -19,7 +19,7 @@
package org.apache.fineract.portfolio.floatingrates.data; package org.apache.fineract.portfolio.floatingrates.data;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDate; import java.time.OffsetDateTime;
import java.util.List; import java.util.List;
import org.apache.commons.lang3.builder.CompareToBuilder; import org.apache.commons.lang3.builder.CompareToBuilder;
import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.EqualsBuilder;
@@ -33,15 +33,15 @@ public class FloatingRateData implements Comparable<FloatingRateData>, Serializa
private final boolean isBaseLendingRate; private final boolean isBaseLendingRate;
private final boolean isActive; private final boolean isActive;
private final String createdBy; private final String createdBy;
private final LocalDate createdOn; private final OffsetDateTime createdOn;
private final String modifiedBy; private final String modifiedBy;
private final LocalDate modifiedOn; private final OffsetDateTime modifiedOn;
private final List<FloatingRatePeriodData> ratePeriods; private final List<FloatingRatePeriodData> ratePeriods;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final List<EnumOptionData> interestRateFrequencyTypeOptions; private final List<EnumOptionData> interestRateFrequencyTypeOptions;
public FloatingRateData(Long id, String name, boolean isBaseLendingRate, boolean isActive, String createdBy, LocalDate createdOn, public FloatingRateData(Long id, String name, boolean isBaseLendingRate, boolean isActive, String createdBy, OffsetDateTime createdOn,
String modifiedBy, LocalDate modifiedOn, List<FloatingRatePeriodData> ratePeriods, String modifiedBy, OffsetDateTime modifiedOn, List<FloatingRatePeriodData> ratePeriods,
List<EnumOptionData> interestRateFrequencyTypeOptions) { List<EnumOptionData> interestRateFrequencyTypeOptions) {
this.id = id; this.id = id;
this.name = name; this.name = name;
@@ -75,7 +75,7 @@ public class FloatingRateData implements Comparable<FloatingRateData>, Serializa
return this.createdBy; return this.createdBy;
} }
public LocalDate getCreatedOn() { public OffsetDateTime getCreatedOn() {
return this.createdOn; return this.createdOn;
} }
@@ -83,7 +83,7 @@ public class FloatingRateData implements Comparable<FloatingRateData>, Serializa
return this.modifiedBy; return this.modifiedBy;
} }
public LocalDate getModifiedOn() { public OffsetDateTime getModifiedOn() {
return this.modifiedOn; return this.modifiedOn;
} }
@@ -21,7 +21,7 @@ package org.apache.fineract.portfolio.floatingrates.data;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.OffsetDateTime;
import org.apache.commons.lang3.builder.CompareToBuilder; import org.apache.commons.lang3.builder.CompareToBuilder;
import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder;
@@ -34,12 +34,12 @@ public class FloatingRatePeriodData implements Comparable<FloatingRatePeriodData
private boolean isDifferentialToBaseLendingRate; private boolean isDifferentialToBaseLendingRate;
private boolean isActive; private boolean isActive;
private String createdBy; private String createdBy;
private LocalDateTime createdOn; private OffsetDateTime createdOn;
private String modifiedBy; private String modifiedBy;
private LocalDateTime modifiedOn; private OffsetDateTime modifiedOn;
public FloatingRatePeriodData(Long id, LocalDate fromDate, BigDecimal interestRate, boolean isDifferentialToBaseLendingRate, public FloatingRatePeriodData(Long id, LocalDate fromDate, BigDecimal interestRate, boolean isDifferentialToBaseLendingRate,
boolean isActive, String createdBy, LocalDateTime createdOn, String modifiedBy, LocalDateTime modifiedOn) { boolean isActive, String createdBy, OffsetDateTime createdOn, String modifiedBy, OffsetDateTime modifiedOn) {
this.id = id; this.id = id;
this.fromDate = fromDate; this.fromDate = fromDate;
this.interestRate = interestRate; this.interestRate = interestRate;
@@ -51,6 +51,15 @@ public class FloatingRatePeriodData implements Comparable<FloatingRatePeriodData
this.modifiedOn = modifiedOn; this.modifiedOn = modifiedOn;
} }
public FloatingRatePeriodData(Long id, LocalDate fromDate, BigDecimal interestRate, boolean isDifferentialToBaseLendingRate,
boolean isActive) {
this.id = id;
this.fromDate = fromDate;
this.interestRate = interestRate;
this.isDifferentialToBaseLendingRate = isDifferentialToBaseLendingRate;
this.isActive = isActive;
}
public Long getId() { public Long getId() {
return this.id; return this.id;
} }
@@ -79,7 +88,7 @@ public class FloatingRatePeriodData implements Comparable<FloatingRatePeriodData
return this.createdBy; return this.createdBy;
} }
public LocalDateTime getCreatedOn() { public OffsetDateTime getCreatedOn() {
return this.createdOn; return this.createdOn;
} }
@@ -87,7 +96,7 @@ public class FloatingRatePeriodData implements Comparable<FloatingRatePeriodData
return this.modifiedBy; return this.modifiedBy;
} }
public LocalDateTime getModifiedOn() { public OffsetDateTime getModifiedOn() {
return this.modifiedOn; return this.modifiedOn;
} }
@@ -23,7 +23,6 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
@@ -34,24 +33,20 @@ import javax.persistence.CascadeType;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.FetchType; import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany; import javax.persistence.OneToMany;
import javax.persistence.OrderBy; import javax.persistence.OrderBy;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.UniqueConstraint; import javax.persistence.UniqueConstraint;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
import org.apache.fineract.infrastructure.core.serialization.JsonParserHelper; import org.apache.fineract.infrastructure.core.serialization.JsonParserHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.floatingrates.data.FloatingRateDTO; import org.apache.fineract.portfolio.floatingrates.data.FloatingRateDTO;
import org.apache.fineract.portfolio.floatingrates.data.FloatingRatePeriodData; import org.apache.fineract.portfolio.floatingrates.data.FloatingRatePeriodData;
import org.apache.fineract.useradministration.domain.AppUser;
//TODO: refactor to use AbstractAuditableCustom!
@Entity @Entity
@Table(name = "m_floating_rates", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "unq_name") }) @Table(name = "m_floating_rates", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "unq_name") })
public class FloatingRate extends AbstractPersistableCustom { public class FloatingRate extends AbstractAuditableWithUTCDateTimeCustom {
@Column(name = "name", length = 200, unique = true, nullable = false) @Column(name = "name", length = 200, unique = true, nullable = false)
private String name; private String name;
@@ -66,34 +61,23 @@ public class FloatingRate extends AbstractPersistableCustom {
@OneToMany(cascade = CascadeType.ALL, mappedBy = "floatingRate", orphanRemoval = true, fetch = FetchType.EAGER) @OneToMany(cascade = CascadeType.ALL, mappedBy = "floatingRate", orphanRemoval = true, fetch = FetchType.EAGER)
private List<FloatingRatePeriod> floatingRatePeriods; private List<FloatingRatePeriod> floatingRatePeriods;
@ManyToOne(optional = true, fetch = FetchType.LAZY) /*
@JoinColumn(name = "createdby_id", nullable = false) * Deprecated since common Auditable fields were introduced. Columns and data left untouched to help migration.
private AppUser createdBy; *
* @Column(name = "created_date", nullable = false) private LocalDateTime createdOn;
@ManyToOne(optional = true, fetch = FetchType.LAZY) *
@JoinColumn(name = "lastmodifiedby_id", nullable = false) * @Column(name = "lastmodified_date", nullable = false) private LocalDateTime modifiedOn;
private AppUser modifiedBy; */
@Column(name = "created_date", nullable = false)
private LocalDateTime createdOn;
@Column(name = "lastmodified_date", nullable = false)
private LocalDateTime modifiedOn;
public FloatingRate() { public FloatingRate() {
} }
public FloatingRate(String name, boolean isBaseLendingRate, boolean isActive, List<FloatingRatePeriod> floatingRatePeriods, public FloatingRate(String name, boolean isBaseLendingRate, boolean isActive, List<FloatingRatePeriod> floatingRatePeriods) {
AppUser createdBy, AppUser modifiedBy, LocalDateTime createdOn, LocalDateTime modifiedOn) {
this.name = name; this.name = name;
this.isBaseLendingRate = isBaseLendingRate; this.isBaseLendingRate = isBaseLendingRate;
this.isActive = isActive; this.isActive = isActive;
this.floatingRatePeriods = floatingRatePeriods; this.floatingRatePeriods = floatingRatePeriods;
this.createdBy = createdBy;
this.createdOn = createdOn;
this.modifiedBy = modifiedBy;
this.modifiedOn = modifiedOn;
if (floatingRatePeriods != null) { if (floatingRatePeriods != null) {
for (FloatingRatePeriod ratePeriod : floatingRatePeriods) { for (FloatingRatePeriod ratePeriod : floatingRatePeriods) {
ratePeriod.updateFloatingRate(this); ratePeriod.updateFloatingRate(this);
@@ -101,20 +85,19 @@ public class FloatingRate extends AbstractPersistableCustom {
} }
} }
public static FloatingRate createNew(AppUser currentUser, JsonCommand command) { public static FloatingRate createNew(JsonCommand command) {
final String name = command.stringValueOfParameterNamed("name"); final String name = command.stringValueOfParameterNamed("name");
final boolean isBaseLendingRate = command.parameterExists("isBaseLendingRate") final boolean isBaseLendingRate = command.parameterExists("isBaseLendingRate")
? command.booleanPrimitiveValueOfParameterNamed("isBaseLendingRate") ? command.booleanPrimitiveValueOfParameterNamed("isBaseLendingRate")
: false; : false;
final boolean isActive = command.parameterExists("isActive") ? command.booleanPrimitiveValueOfParameterNamed("isActive") : true; final boolean isActive = command.parameterExists("isActive") ? command.booleanPrimitiveValueOfParameterNamed("isActive") : true;
final List<FloatingRatePeriod> floatingRatePeriods = getRatePeriods(currentUser, command); final List<FloatingRatePeriod> floatingRatePeriods = getRatePeriods(command);
final LocalDateTime currentDate = DateUtils.getLocalDateTimeOfSystem();
return new FloatingRate(name, isBaseLendingRate, isActive, floatingRatePeriods, currentUser, currentUser, currentDate, currentDate); return new FloatingRate(name, isBaseLendingRate, isActive, floatingRatePeriods);
} }
private static List<FloatingRatePeriod> getRatePeriods(final AppUser currentUser, final JsonCommand command) { private static List<FloatingRatePeriod> getRatePeriods(final JsonCommand command) {
if (!command.parameterExists("ratePeriods")) { if (!command.parameterExists("ratePeriods")) {
return null; return null;
} }
@@ -129,9 +112,7 @@ public class FloatingRate extends AbstractPersistableCustom {
? ratePeriodObject.get("isDifferentialToBaseLendingRate").getAsBoolean() ? ratePeriodObject.get("isDifferentialToBaseLendingRate").getAsBoolean()
: false; : false;
final boolean isActive = true; final boolean isActive = true;
final LocalDateTime currentDate = DateUtils.getLocalDateTimeOfSystem(); ratePeriods.add(new FloatingRatePeriod(fromDate, interestRate, isDifferentialToBaseLendingRate, isActive));
ratePeriods.add(new FloatingRatePeriod(fromDate, interestRate, isDifferentialToBaseLendingRate, isActive, currentUser,
currentUser, currentDate, currentDate));
} }
return ratePeriods; return ratePeriods;
@@ -153,23 +134,7 @@ public class FloatingRate extends AbstractPersistableCustom {
return this.floatingRatePeriods; return this.floatingRatePeriods;
} }
public AppUser getCreatedBy() { public Map<String, Object> update(final JsonCommand command) {
return this.createdBy;
}
public AppUser getModifiedBy() {
return this.modifiedBy;
}
public LocalDateTime getCreatedOn() {
return this.createdOn;
}
public LocalDateTime getModifiedOn() {
return this.modifiedOn;
}
public Map<String, Object> update(final JsonCommand command, final AppUser appUser) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(9); final Map<String, Object> actualChanges = new LinkedHashMap<>(9);
@@ -191,24 +156,22 @@ public class FloatingRate extends AbstractPersistableCustom {
this.isActive = newValue; this.isActive = newValue;
} }
final List<FloatingRatePeriod> newRatePeriods = getRatePeriods(appUser, command); final List<FloatingRatePeriod> newRatePeriods = getRatePeriods(command);
if (newRatePeriods != null && !newRatePeriods.isEmpty()) { if (newRatePeriods != null && !newRatePeriods.isEmpty()) {
updateRatePeriods(newRatePeriods, appUser); updateRatePeriods(newRatePeriods);
actualChanges.put("ratePeriods", command.jsonFragment("ratePeriods")); actualChanges.put("ratePeriods", command.jsonFragment("ratePeriods"));
} }
return actualChanges; return actualChanges;
} }
private void updateRatePeriods(final List<FloatingRatePeriod> newRatePeriods, final AppUser appUser) { private void updateRatePeriods(final List<FloatingRatePeriod> newRatePeriods) {
final LocalDate today = DateUtils.getBusinessLocalDate(); final LocalDate today = DateUtils.getBusinessLocalDate();
if (this.floatingRatePeriods != null) { if (this.floatingRatePeriods != null) {
for (FloatingRatePeriod ratePeriod : this.floatingRatePeriods) { for (FloatingRatePeriod ratePeriod : this.floatingRatePeriods) {
LocalDate fromDate = ratePeriod.getFromDate(); LocalDate fromDate = ratePeriod.getFromDate();
if (fromDate.isAfter(today)) { if (fromDate.isAfter(today)) {
ratePeriod.setActive(false); ratePeriod.setActive(false);
ratePeriod.setModifiedBy(appUser);
ratePeriod.setModifiedOn(DateUtils.getLocalDateTimeOfSystem());
} }
} }
} }
@@ -20,22 +20,18 @@ package org.apache.fineract.portfolio.floatingrates.domain;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne; import javax.persistence.ManyToOne;
import javax.persistence.Table; import javax.persistence.Table;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
import org.apache.fineract.portfolio.floatingrates.data.FloatingRateDTO; import org.apache.fineract.portfolio.floatingrates.data.FloatingRateDTO;
import org.apache.fineract.portfolio.floatingrates.data.FloatingRatePeriodData; import org.apache.fineract.portfolio.floatingrates.data.FloatingRatePeriodData;
import org.apache.fineract.useradministration.domain.AppUser;
//TODO: refactor to use AbstractAuditableCustom!
@Entity @Entity
@Table(name = "m_floating_rates_periods") @Table(name = "m_floating_rates_periods")
public class FloatingRatePeriod extends AbstractPersistableCustom { public class FloatingRatePeriod extends AbstractAuditableWithUTCDateTimeCustom {
@ManyToOne @ManyToOne
@JoinColumn(name = "floating_rates_id", nullable = false) @JoinColumn(name = "floating_rates_id", nullable = false)
@@ -53,41 +49,30 @@ public class FloatingRatePeriod extends AbstractPersistableCustom {
@Column(name = "is_active", nullable = false) @Column(name = "is_active", nullable = false)
private boolean isActive; private boolean isActive;
@ManyToOne(optional = true, fetch = FetchType.LAZY) /*
@JoinColumn(name = "createdby_id", nullable = false) * Deprecated since common Auditable fields were introduced. Columns and data left untouched to help migration.
private AppUser createdBy; *
* @Column(name = "created_date", nullable = false) private LocalDateTime createdOn;
@ManyToOne(optional = true, fetch = FetchType.LAZY) *
@JoinColumn(name = "lastmodifiedby_id", nullable = false) * @Column(name = "lastmodified_date", nullable = false) private LocalDateTime modifiedOn;
private AppUser modifiedBy; */
@Column(name = "created_date", nullable = false)
private LocalDateTime createdOn;
@Column(name = "lastmodified_date", nullable = false)
private LocalDateTime modifiedOn;
public FloatingRatePeriod() { public FloatingRatePeriod() {
} }
public FloatingRatePeriod(LocalDate fromDate, BigDecimal interestRate, boolean isDifferentialToBaseLendingRate, boolean isActive, public FloatingRatePeriod(LocalDate fromDate, BigDecimal interestRate, boolean isDifferentialToBaseLendingRate, boolean isActive) {
AppUser createdBy, AppUser modifiedBy, LocalDateTime createdOn, LocalDateTime modifiedOn) {
this.fromDate = fromDate; this.fromDate = fromDate;
this.interestRate = interestRate; this.interestRate = interestRate;
this.isDifferentialToBaseLendingRate = isDifferentialToBaseLendingRate; this.isDifferentialToBaseLendingRate = isDifferentialToBaseLendingRate;
this.isActive = isActive; this.isActive = isActive;
this.createdBy = createdBy;
this.modifiedBy = modifiedBy;
this.createdOn = createdOn;
this.modifiedOn = modifiedOn;
} }
public void updateFloatingRate(FloatingRate floatingRate) { public void updateFloatingRate(FloatingRate floatingRate) {
this.floatingRate = floatingRate; this.floatingRate = floatingRate;
} }
public FloatingRate getFloatingRatesId() { public FloatingRate getFloatingRate() {
return this.floatingRate; return this.floatingRate;
} }
@@ -107,32 +92,8 @@ public class FloatingRatePeriod extends AbstractPersistableCustom {
return this.isActive; return this.isActive;
} }
public AppUser getCreatedBy() { public void setActive(boolean isActive) {
return this.createdBy; this.isActive = isActive;
}
public AppUser getModifiedBy() {
return this.modifiedBy;
}
public LocalDateTime getCreatedOn() {
return this.createdOn;
}
public LocalDateTime getModifiedOn() {
return this.modifiedOn;
}
public void setModifiedBy(AppUser modifiedBy) {
this.modifiedBy = modifiedBy;
}
public void setModifiedOn(LocalDateTime modifiedOn) {
this.modifiedOn = modifiedOn;
}
public void setActive(boolean b) {
this.isActive = b;
} }
public LocalDate fetchFromDate() { public LocalDate fetchFromDate() {
@@ -147,13 +108,7 @@ public class FloatingRatePeriod extends AbstractPersistableCustom {
} }
final LocalDate fromDate = getFromDate(); final LocalDate fromDate = getFromDate();
final LocalDateTime createdOn = getCreatedOn(); return new FloatingRatePeriodData(getId(), fromDate, interest, isDifferentialToBaseLendingRate(), isActive());
final LocalDateTime modifiedOn = getModifiedOn();
String createdBy = getCreatedBy() != null ? getCreatedBy().getUsername() : null;
String modifiedBy = getModifiedBy() != null ? getModifiedBy().getUsername() : null;
return new FloatingRatePeriodData(getId(), fromDate, interest, isDifferentialToBaseLendingRate(), isActive(), createdBy, createdOn,
modifiedBy, modifiedOn);
} }
} }
@@ -25,11 +25,9 @@ import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.floatingrates.domain.FloatingRate; import org.apache.fineract.portfolio.floatingrates.domain.FloatingRate;
import org.apache.fineract.portfolio.floatingrates.domain.FloatingRateRepositoryWrapper; import org.apache.fineract.portfolio.floatingrates.domain.FloatingRateRepositoryWrapper;
import org.apache.fineract.portfolio.floatingrates.serialization.FloatingRateDataValidator; import org.apache.fineract.portfolio.floatingrates.serialization.FloatingRateDataValidator;
import org.apache.fineract.useradministration.domain.AppUser;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -42,14 +40,12 @@ import org.springframework.transaction.annotation.Transactional;
public class FloatingRateWritePlatformServiceImpl implements FloatingRateWritePlatformService { public class FloatingRateWritePlatformServiceImpl implements FloatingRateWritePlatformService {
private static final Logger LOG = LoggerFactory.getLogger(FloatingRateWritePlatformServiceImpl.class); private static final Logger LOG = LoggerFactory.getLogger(FloatingRateWritePlatformServiceImpl.class);
private final PlatformSecurityContext context;
private final FloatingRateDataValidator fromApiJsonDeserializer; private final FloatingRateDataValidator fromApiJsonDeserializer;
private final FloatingRateRepositoryWrapper floatingRateRepository; private final FloatingRateRepositoryWrapper floatingRateRepository;
@Autowired @Autowired
public FloatingRateWritePlatformServiceImpl(final PlatformSecurityContext context, public FloatingRateWritePlatformServiceImpl(final FloatingRateDataValidator fromApiJsonDeserializer,
final FloatingRateDataValidator fromApiJsonDeserializer, final FloatingRateRepositoryWrapper floatingRateRepository) { final FloatingRateRepositoryWrapper floatingRateRepository) {
this.context = context;
this.fromApiJsonDeserializer = fromApiJsonDeserializer; this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.floatingRateRepository = floatingRateRepository; this.floatingRateRepository = floatingRateRepository;
} }
@@ -59,8 +55,7 @@ public class FloatingRateWritePlatformServiceImpl implements FloatingRateWritePl
public CommandProcessingResult createFloatingRate(final JsonCommand command) { public CommandProcessingResult createFloatingRate(final JsonCommand command) {
try { try {
this.fromApiJsonDeserializer.validateForCreate(command.json()); this.fromApiJsonDeserializer.validateForCreate(command.json());
final AppUser currentUser = this.context.authenticatedUser(); final FloatingRate newFloatingRate = FloatingRate.createNew(command);
final FloatingRate newFloatingRate = FloatingRate.createNew(currentUser, command);
this.floatingRateRepository.saveAndFlush(newFloatingRate); this.floatingRateRepository.saveAndFlush(newFloatingRate);
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) // .withCommandId(command.commandId()) //
@@ -82,8 +77,7 @@ public class FloatingRateWritePlatformServiceImpl implements FloatingRateWritePl
try { try {
final FloatingRate floatingRateForUpdate = this.floatingRateRepository.findOneWithNotFoundDetection(command.entityId()); final FloatingRate floatingRateForUpdate = this.floatingRateRepository.findOneWithNotFoundDetection(command.entityId());
this.fromApiJsonDeserializer.validateForUpdate(command.json(), floatingRateForUpdate); this.fromApiJsonDeserializer.validateForUpdate(command.json(), floatingRateForUpdate);
final AppUser currentUser = this.context.authenticatedUser(); final Map<String, Object> changes = floatingRateForUpdate.update(command);
final Map<String, Object> changes = floatingRateForUpdate.update(command, currentUser);
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
this.floatingRateRepository.save(floatingRateForUpdate); this.floatingRateRepository.save(floatingRateForUpdate);
@@ -24,8 +24,10 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.util.List; import java.util.List;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.floatingrates.data.FloatingRateData; import org.apache.fineract.portfolio.floatingrates.data.FloatingRateData;
import org.apache.fineract.portfolio.floatingrates.data.FloatingRatePeriodData; import org.apache.fineract.portfolio.floatingrates.data.FloatingRatePeriodData;
import org.apache.fineract.portfolio.floatingrates.data.InterestRatePeriodData; import org.apache.fineract.portfolio.floatingrates.data.InterestRatePeriodData;
@@ -106,7 +108,8 @@ public class FloatingRatesReadPlatformServiceImpl implements FloatingRatesReadPl
private final StringBuilder sqlQuery = new StringBuilder().append("rate.id as id, ").append("rate.name as name, ") private final StringBuilder sqlQuery = new StringBuilder().append("rate.id as id, ").append("rate.name as name, ")
.append("rate.is_base_lending_rate as isBaseLendingRate, ").append("rate.is_active as isActive, ") .append("rate.is_base_lending_rate as isBaseLendingRate, ").append("rate.is_active as isActive, ")
.append("crappu.username as createdBy, ").append("rate.created_date as createdOn, ") .append("crappu.username as createdBy, ").append("rate.created_date as createdOn, ")
.append("moappu.username as modifiedBy, ").append("rate.lastmodified_date as modifiedOn ") .append("rate.created_on_utc as createdOnUTC, ").append("moappu.username as modifiedBy, ")
.append("rate.lastmodified_date as modifiedOn, ").append("rate.last_modified_on_utc as modifiedOnUTC ")
.append("FROM m_floating_rates as rate ").append("LEFT JOIN m_appuser as crappu on rate.createdby_id = crappu.id ") .append("FROM m_floating_rates as rate ").append("LEFT JOIN m_appuser as crappu on rate.createdby_id = crappu.id ")
.append("LEFT JOIN m_appuser as moappu on rate.lastmodifiedby_id = moappu.id "); .append("LEFT JOIN m_appuser as moappu on rate.lastmodifiedby_id = moappu.id ");
@@ -121,10 +124,16 @@ public class FloatingRatesReadPlatformServiceImpl implements FloatingRatesReadPl
final boolean isBaseLendingRate = rs.getBoolean("isBaseLendingRate"); final boolean isBaseLendingRate = rs.getBoolean("isBaseLendingRate");
final boolean isActive = rs.getBoolean("isActive"); final boolean isActive = rs.getBoolean("isActive");
final String createdBy = rs.getString("createdBy"); final String createdBy = rs.getString("createdBy");
final LocalDate createdOn = JdbcSupport.getLocalDate(rs, "createdOn"); final LocalDateTime createdOnLocal = JdbcSupport.getLocalDateTime(rs, "createdOn");
final OffsetDateTime createdOnUtc = JdbcSupport.getOffsetDateTime(rs, "createdOnUTC");
final String modifiedBy = rs.getString("modifiedBy"); final String modifiedBy = rs.getString("modifiedBy");
final LocalDate modifiedOn = JdbcSupport.getLocalDate(rs, "modifiedOn"); final LocalDateTime modifiedOnLocal = JdbcSupport.getLocalDateTime(rs, "modifiedOn");
final OffsetDateTime modifiedOnUtc = JdbcSupport.getOffsetDateTime(rs, "modifiedOnUTC");
List<FloatingRatePeriodData> ratePeriods = null; List<FloatingRatePeriodData> ratePeriods = null;
final OffsetDateTime createdOn = createdOnUtc != null ? createdOnUtc
: OffsetDateTime.of(createdOnLocal, DateUtils.getDateTimeZoneOfTenant().getRules().getOffset(createdOnLocal));
final OffsetDateTime modifiedOn = modifiedOnUtc != null ? modifiedOnUtc
: OffsetDateTime.of(modifiedOnLocal, DateUtils.getDateTimeZoneOfTenant().getRules().getOffset(modifiedOnLocal));
if (addRatePeriods) { if (addRatePeriods) {
FloatingRatePeriodRowMapper ratePeriodMapper = new FloatingRatePeriodRowMapper(); FloatingRatePeriodRowMapper ratePeriodMapper = new FloatingRatePeriodRowMapper();
final String sql = "select " + ratePeriodMapper.schema() final String sql = "select " + ratePeriodMapper.schema()
@@ -146,8 +155,9 @@ public class FloatingRatesReadPlatformServiceImpl implements FloatingRatesReadPl
.append("period.interest_rate as interestRate, ") .append("period.interest_rate as interestRate, ")
.append("period.is_differential_to_base_lending_rate as isDifferentialToBaseLendingRate, ") .append("period.is_differential_to_base_lending_rate as isDifferentialToBaseLendingRate, ")
.append("period.is_active as isActive, ").append("crappu.username as createdBy, ") .append("period.is_active as isActive, ").append("crappu.username as createdBy, ")
.append("period.created_date as createdOn, ").append("moappu.username as modifiedBy, ") .append("period.created_date as createdOn, ").append("rate.created_on_utc as createdOnUTC, ")
.append("period.lastmodified_date as modifiedOn ").append("FROM m_floating_rates_periods as period ") .append("moappu.username as modifiedBy, ").append("period.lastmodified_date as modifiedOn, ")
.append("rate.last_modified_on_utc as modifiedOnUTC ").append("FROM m_floating_rates_periods as period ")
.append("LEFT JOIN m_appuser as crappu on period.createdby_id = crappu.id ") .append("LEFT JOIN m_appuser as crappu on period.createdby_id = crappu.id ")
.append("LEFT JOIN m_appuser as moappu on period.lastmodifiedby_id = moappu.id "); .append("LEFT JOIN m_appuser as moappu on period.lastmodifiedby_id = moappu.id ");
@@ -159,9 +169,15 @@ public class FloatingRatesReadPlatformServiceImpl implements FloatingRatesReadPl
final boolean isDifferentialToBaseLendingRate = rs.getBoolean("isDifferentialToBaseLendingRate"); final boolean isDifferentialToBaseLendingRate = rs.getBoolean("isDifferentialToBaseLendingRate");
final boolean isActive = rs.getBoolean("isActive"); final boolean isActive = rs.getBoolean("isActive");
final String createdBy = rs.getString("createdBy"); final String createdBy = rs.getString("createdBy");
final LocalDateTime createdOn = JdbcSupport.getLocalDateTime(rs, "createdOn"); final LocalDateTime createdOnLocal = JdbcSupport.getLocalDateTime(rs, "createdOn");
final OffsetDateTime createdOnUtc = JdbcSupport.getOffsetDateTime(rs, "createdOnUTC");
final String modifiedBy = rs.getString("modifiedBy"); final String modifiedBy = rs.getString("modifiedBy");
final LocalDateTime modifiedOn = JdbcSupport.getLocalDateTime(rs, "modifiedOn"); final LocalDateTime modifiedOnLocal = JdbcSupport.getLocalDateTime(rs, "modifiedOn");
final OffsetDateTime modifiedOnUtc = JdbcSupport.getOffsetDateTime(rs, "modifiedOnUTC");
final OffsetDateTime createdOn = createdOnUtc != null ? createdOnUtc
: OffsetDateTime.of(createdOnLocal, DateUtils.getDateTimeZoneOfTenant().getRules().getOffset(createdOnLocal));
final OffsetDateTime modifiedOn = modifiedOnUtc != null ? modifiedOnUtc
: OffsetDateTime.of(modifiedOnLocal, DateUtils.getDateTimeZoneOfTenant().getRules().getOffset(modifiedOnLocal));
return new FloatingRatePeriodData(id, fromDate, interestRate, isDifferentialToBaseLendingRate, isActive, createdBy, createdOn, return new FloatingRatePeriodData(id, fromDate, interestRate, isDifferentialToBaseLendingRate, isActive, createdBy, createdOn,
modifiedBy, modifiedOn); modifiedBy, modifiedOn);
} }
@@ -45,15 +45,15 @@ public class AddAccrualEntriesTasklet implements Tasklet {
@Override @Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
Collection<LoanScheduleAccrualData> loanScheduleAccrualDatas = loanReadPlatformService.retriveScheduleAccrualData(); Collection<LoanScheduleAccrualData> loanScheduleAccrualDataList = loanReadPlatformService.retriveScheduleAccrualData();
Map<Long, Collection<LoanScheduleAccrualData>> loanDataMap = new HashMap<>(); Map<Long, Collection<LoanScheduleAccrualData>> loanDataMap = new HashMap<>();
for (final LoanScheduleAccrualData accrualData : loanScheduleAccrualDatas) { for (final LoanScheduleAccrualData accrualData : loanScheduleAccrualDataList) {
if (loanDataMap.containsKey(accrualData.getLoanId())) { if (loanDataMap.containsKey(accrualData.getLoanId())) {
loanDataMap.get(accrualData.getLoanId()).add(accrualData); loanDataMap.get(accrualData.getLoanId()).add(accrualData);
} else { } else {
Collection<LoanScheduleAccrualData> accrualDatas = new ArrayList<>(); Collection<LoanScheduleAccrualData> accrualDataList = new ArrayList<>();
accrualDatas.add(accrualData); accrualDataList.add(accrualData);
loanDataMap.put(accrualData.getLoanId(), accrualDatas); loanDataMap.put(accrualData.getLoanId(), accrualDataList);
} }
} }
@@ -62,7 +62,7 @@ public class AddAccrualEntriesTasklet implements Tasklet {
try { try {
loanAccrualWritePlatformService.addAccrualAccounting(mapEntry.getKey(), mapEntry.getValue()); loanAccrualWritePlatformService.addAccrualAccounting(mapEntry.getKey(), mapEntry.getValue());
} catch (Exception e) { } catch (Exception e) {
log.error("Failed to add accural transaction for loan {}", mapEntry.getKey(), e); log.error("Failed to add accrual transaction for loan {}", mapEntry.getKey(), e);
errors.add(e); errors.add(e);
} }
} }
@@ -39,31 +39,31 @@ public class LoanAccrualPlatformServiceImpl implements LoanAccrualPlatformServic
private final LoanAccrualWritePlatformService loanAccrualWritePlatformService; private final LoanAccrualWritePlatformService loanAccrualWritePlatformService;
@Override @Override
public void addPeriodicAccruals(final LocalDate tilldate) throws JobExecutionException { public void addPeriodicAccruals(final LocalDate tillDate) throws JobExecutionException {
Collection<LoanScheduleAccrualData> loanScheduleAccrualDatas = this.loanReadPlatformService.retrivePeriodicAccrualData(tilldate); Collection<LoanScheduleAccrualData> loanScheduleAccrualDataList = this.loanReadPlatformService.retrivePeriodicAccrualData(tillDate);
addPeriodicAccruals(tilldate, loanScheduleAccrualDatas); addPeriodicAccruals(tillDate, loanScheduleAccrualDataList);
} }
@Override @Override
public void addPeriodicAccruals(final LocalDate tilldate, Collection<LoanScheduleAccrualData> loanScheduleAccrualDatas) public void addPeriodicAccruals(final LocalDate tillDate, Collection<LoanScheduleAccrualData> loanScheduleAccrualDataList)
throws JobExecutionException { throws JobExecutionException {
Map<Long, Collection<LoanScheduleAccrualData>> loanDataMap = new HashMap<>(); Map<Long, Collection<LoanScheduleAccrualData>> loanDataMap = new HashMap<>();
for (final LoanScheduleAccrualData accrualData : loanScheduleAccrualDatas) { for (final LoanScheduleAccrualData accrualData : loanScheduleAccrualDataList) {
if (loanDataMap.containsKey(accrualData.getLoanId())) { if (loanDataMap.containsKey(accrualData.getLoanId())) {
loanDataMap.get(accrualData.getLoanId()).add(accrualData); loanDataMap.get(accrualData.getLoanId()).add(accrualData);
} else { } else {
Collection<LoanScheduleAccrualData> accrualDatas = new ArrayList<>(); Collection<LoanScheduleAccrualData> accrualDataList = new ArrayList<>();
accrualDatas.add(accrualData); accrualDataList.add(accrualData);
loanDataMap.put(accrualData.getLoanId(), accrualDatas); loanDataMap.put(accrualData.getLoanId(), accrualDataList);
} }
} }
List<Throwable> errors = new ArrayList<>(); List<Throwable> errors = new ArrayList<>();
for (Map.Entry<Long, Collection<LoanScheduleAccrualData>> mapEntry : loanDataMap.entrySet()) { for (Map.Entry<Long, Collection<LoanScheduleAccrualData>> mapEntry : loanDataMap.entrySet()) {
try { try {
this.loanAccrualWritePlatformService.addPeriodicAccruals(tilldate, mapEntry.getKey(), mapEntry.getValue()); this.loanAccrualWritePlatformService.addPeriodicAccruals(tillDate, mapEntry.getKey(), mapEntry.getValue());
} catch (Exception e) { } catch (Exception e) {
log.error("Failed to add accural transaction for loan {}", mapEntry.getKey(), e); log.error("Failed to add accrual transaction for loan {}", mapEntry.getKey(), e);
errors.add(e); errors.add(e);
} }
} }
@@ -32,6 +32,7 @@ import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService; import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency; import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper; import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
@@ -47,6 +48,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException; import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations; import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
import org.apache.fineract.useradministration.domain.AppUser;
import org.apache.fineract.useradministration.domain.AppUserRepositoryWrapper; import org.apache.fineract.useradministration.domain.AppUserRepositoryWrapper;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
@@ -63,63 +65,62 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
private final DatabaseSpecificSQLGenerator sqlGenerator; private final DatabaseSpecificSQLGenerator sqlGenerator;
private final JournalEntryWritePlatformService journalEntryWritePlatformService; private final JournalEntryWritePlatformService journalEntryWritePlatformService;
private final AppUserRepositoryWrapper userRepository; private final AppUserRepositoryWrapper userRepository;
private final PlatformSecurityContext context;
private final LoanRepositoryWrapper loanRepositoryWrapper; private final LoanRepositoryWrapper loanRepositoryWrapper;
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository; private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
@Override @Override
@Transactional @Transactional
public void addAccrualAccounting(final Long loanId, final Collection<LoanScheduleAccrualData> loanScheduleAccrualDatas) public void addAccrualAccounting(final Long loanId, final Collection<LoanScheduleAccrualData> loanScheduleAccrualData) {
throws Exception {
Collection<LoanChargeData> chargeData = this.loanChargeReadPlatformService.retrieveLoanChargesForAccural(loanId); Collection<LoanChargeData> chargeData = this.loanChargeReadPlatformService.retrieveLoanChargesForAccural(loanId);
Collection<LoanSchedulePeriodData> loanWaiverScheduleData = new ArrayList<>(1); Collection<LoanSchedulePeriodData> loanWaiverScheduleData = new ArrayList<>(1);
Collection<LoanTransactionData> loanWaiverTansactionData = new ArrayList<>(1); Collection<LoanTransactionData> loanWaiverTransactionData = new ArrayList<>(1);
for (final LoanScheduleAccrualData accrualData : loanScheduleAccrualDatas) { for (final LoanScheduleAccrualData accrualData : loanScheduleAccrualData) {
if (accrualData.getWaivedInterestIncome() != null && loanWaiverScheduleData.isEmpty()) { if (accrualData.getWaivedInterestIncome() != null && loanWaiverScheduleData.isEmpty()) {
loanWaiverScheduleData = this.loanReadPlatformService.fetchWaiverInterestRepaymentData(accrualData.getLoanId()); loanWaiverScheduleData = this.loanReadPlatformService.fetchWaiverInterestRepaymentData(accrualData.getLoanId());
loanWaiverTansactionData = this.loanReadPlatformService.retrieveWaiverLoanTransactions(accrualData.getLoanId()); loanWaiverTransactionData = this.loanReadPlatformService.retrieveWaiverLoanTransactions(accrualData.getLoanId());
} }
updateCharges(chargeData, accrualData, accrualData.getFromDateAsLocaldate(), accrualData.getDueDateAsLocaldate()); updateCharges(chargeData, accrualData, accrualData.getFromDateAsLocaldate(), accrualData.getDueDateAsLocaldate());
updateInterestIncome(accrualData, loanWaiverTansactionData, loanWaiverScheduleData, accrualData.getDueDateAsLocaldate()); updateInterestIncome(accrualData, loanWaiverTransactionData, loanWaiverScheduleData, accrualData.getDueDateAsLocaldate());
addAccrualAccounting(accrualData); addAccrualAccounting(accrualData);
} }
} }
@Override @Override
@Transactional @Transactional
public void addPeriodicAccruals(final LocalDate tilldate, Long loanId, Collection<LoanScheduleAccrualData> loanScheduleAccrualDatas) public void addPeriodicAccruals(final LocalDate tillDate, Long loanId, Collection<LoanScheduleAccrualData> loanScheduleAccrualData) {
throws Exception {
boolean firstTime = true; boolean firstTime = true;
LocalDate accruredTill = null; LocalDate accruedTill = null;
Collection<LoanChargeData> chargeData = this.loanChargeReadPlatformService.retrieveLoanChargesForAccural(loanId); Collection<LoanChargeData> chargeData = this.loanChargeReadPlatformService.retrieveLoanChargesForAccural(loanId);
Collection<LoanSchedulePeriodData> loanWaiverScheduleData = new ArrayList<>(1); Collection<LoanSchedulePeriodData> loanWaiverScheduleData = new ArrayList<>(1);
Collection<LoanTransactionData> loanWaiverTansactionData = new ArrayList<>(1); Collection<LoanTransactionData> loanWaiverTransactionData = new ArrayList<>(1);
for (final LoanScheduleAccrualData accrualData : loanScheduleAccrualDatas) { for (final LoanScheduleAccrualData accrualData : loanScheduleAccrualData) {
if (accrualData.getWaivedInterestIncome() != null && loanWaiverScheduleData.isEmpty()) { if (accrualData.getWaivedInterestIncome() != null && loanWaiverScheduleData.isEmpty()) {
loanWaiverScheduleData = this.loanReadPlatformService.fetchWaiverInterestRepaymentData(accrualData.getLoanId()); loanWaiverScheduleData = this.loanReadPlatformService.fetchWaiverInterestRepaymentData(accrualData.getLoanId());
loanWaiverTansactionData = this.loanReadPlatformService.retrieveWaiverLoanTransactions(accrualData.getLoanId()); loanWaiverTransactionData = this.loanReadPlatformService.retrieveWaiverLoanTransactions(accrualData.getLoanId());
} }
if (accrualData.getDueDateAsLocaldate().isAfter(tilldate)) { if (accrualData.getDueDateAsLocaldate().isAfter(tillDate)) {
if (accruredTill == null || firstTime) { if (accruedTill == null || firstTime) {
accruredTill = accrualData.getAccruedTill(); accruedTill = accrualData.getAccruedTill();
firstTime = false; firstTime = false;
} }
if (accruredTill == null || accruredTill.isBefore(tilldate)) { if (accruedTill == null || accruedTill.isBefore(tillDate)) {
updateCharges(chargeData, accrualData, accrualData.getFromDateAsLocaldate(), tilldate); updateCharges(chargeData, accrualData, accrualData.getFromDateAsLocaldate(), tillDate);
updateInterestIncome(accrualData, loanWaiverTansactionData, loanWaiverScheduleData, tilldate); updateInterestIncome(accrualData, loanWaiverTransactionData, loanWaiverScheduleData, tillDate);
addAccrualTillSpecificDate(tilldate, accrualData); addAccrualTillSpecificDate(tillDate, accrualData);
} }
} else { } else {
updateCharges(chargeData, accrualData, accrualData.getFromDateAsLocaldate(), accrualData.getDueDateAsLocaldate()); updateCharges(chargeData, accrualData, accrualData.getFromDateAsLocaldate(), accrualData.getDueDateAsLocaldate());
updateInterestIncome(accrualData, loanWaiverTansactionData, loanWaiverScheduleData, tilldate); updateInterestIncome(accrualData, loanWaiverTransactionData, loanWaiverScheduleData, tillDate);
addAccrualAccounting(accrualData); addAccrualAccounting(accrualData);
accruredTill = accrualData.getDueDateAsLocaldate(); accruedTill = accrualData.getDueDateAsLocaldate();
} }
} }
} }
private void addAccrualTillSpecificDate(final LocalDate tilldate, final LoanScheduleAccrualData accrualData) throws Exception { private void addAccrualTillSpecificDate(final LocalDate tillDate, final LoanScheduleAccrualData accrualData) {
LocalDate interestStartDate = accrualData.getFromDateAsLocaldate(); LocalDate interestStartDate = accrualData.getFromDateAsLocaldate();
if (accrualData.getInterestCalculatedFrom() != null if (accrualData.getInterestCalculatedFrom() != null
&& accrualData.getFromDateAsLocaldate().isBefore(accrualData.getInterestCalculatedFrom())) { && accrualData.getFromDateAsLocaldate().isBefore(accrualData.getInterestCalculatedFrom())) {
@@ -133,150 +134,150 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
int totalNumberOfDays = Math.toIntExact(ChronoUnit.DAYS.between(interestStartDate, accrualData.getDueDateAsLocaldate())); int totalNumberOfDays = Math.toIntExact(ChronoUnit.DAYS.between(interestStartDate, accrualData.getDueDateAsLocaldate()));
LocalDate startDate = accrualData.getFromDateAsLocaldate(); LocalDate startDate = accrualData.getFromDateAsLocaldate();
if (accrualData.getInterestCalculatedFrom() != null && startDate.isBefore(accrualData.getInterestCalculatedFrom())) { if (accrualData.getInterestCalculatedFrom() != null && startDate.isBefore(accrualData.getInterestCalculatedFrom())) {
if (accrualData.getInterestCalculatedFrom().isBefore(tilldate)) { if (accrualData.getInterestCalculatedFrom().isBefore(tillDate)) {
startDate = accrualData.getInterestCalculatedFrom(); startDate = accrualData.getInterestCalculatedFrom();
} else { } else {
startDate = tilldate; startDate = tillDate;
} }
} }
int daysToBeAccrued = Math.toIntExact(ChronoUnit.DAYS.between(startDate, tilldate)); int daysToBeAccrued = Math.toIntExact(ChronoUnit.DAYS.between(startDate, tillDate));
double interestPerDay = accrualData.getAccruableIncome().doubleValue() / totalNumberOfDays; double interestPerDay = accrualData.getAccruableIncome().doubleValue() / totalNumberOfDays;
BigDecimal amount = BigDecimal.ZERO; BigDecimal amount = BigDecimal.ZERO;
BigDecimal interestportion = null; BigDecimal interestPortion;
BigDecimal feeportion = accrualData.getDueDateFeeIncome(); BigDecimal feePortion = accrualData.getDueDateFeeIncome();
BigDecimal penaltyportion = accrualData.getDueDatePenaltyIncome(); BigDecimal penaltyPortion = accrualData.getDueDatePenaltyIncome();
if (daysToBeAccrued >= totalNumberOfDays) { if (daysToBeAccrued >= totalNumberOfDays) {
interestportion = accrualData.getAccruableIncome(); interestPortion = accrualData.getAccruableIncome();
} else { } else {
double iterest = interestPerDay * daysToBeAccrued; interestPortion = BigDecimal.valueOf(interestPerDay * daysToBeAccrued);
interestportion = BigDecimal.valueOf(iterest);
} }
interestportion = interestportion.setScale(accrualData.getCurrencyData().decimalPlaces(), MoneyHelper.getRoundingMode()); interestPortion = interestPortion.setScale(accrualData.getCurrencyData().decimalPlaces(), MoneyHelper.getRoundingMode());
BigDecimal totalAccInterest = accrualData.getAccruedInterestIncome(); BigDecimal totalAccInterest = accrualData.getAccruedInterestIncome();
BigDecimal totalAccPenalty = accrualData.getAccruedPenaltyIncome(); BigDecimal totalAccPenalty = accrualData.getAccruedPenaltyIncome();
BigDecimal totalAccFee = accrualData.getAccruedFeeIncome(); BigDecimal totalAccFee = accrualData.getAccruedFeeIncome();
if (interestportion != null) { if (totalAccInterest == null) {
if (totalAccInterest == null) { totalAccInterest = BigDecimal.ZERO;
totalAccInterest = BigDecimal.ZERO;
}
interestportion = interestportion.subtract(totalAccInterest);
amount = amount.add(interestportion);
totalAccInterest = totalAccInterest.add(interestportion);
if (interestportion.compareTo(BigDecimal.ZERO) == 0) {
interestportion = null;
}
} }
if (feeportion != null) { interestPortion = interestPortion.subtract(totalAccInterest);
amount = amount.add(interestPortion);
totalAccInterest = totalAccInterest.add(interestPortion);
if (interestPortion.compareTo(BigDecimal.ZERO) == 0) {
interestPortion = null;
}
if (feePortion != null) {
if (totalAccFee == null) { if (totalAccFee == null) {
totalAccFee = BigDecimal.ZERO; totalAccFee = BigDecimal.ZERO;
} }
feeportion = feeportion.subtract(totalAccFee); feePortion = feePortion.subtract(totalAccFee);
amount = amount.add(feeportion); amount = amount.add(feePortion);
totalAccFee = totalAccFee.add(feeportion); totalAccFee = totalAccFee.add(feePortion);
if (feeportion.compareTo(BigDecimal.ZERO) == 0) { if (feePortion.compareTo(BigDecimal.ZERO) == 0) {
feeportion = null; feePortion = null;
} }
} }
if (penaltyportion != null) { if (penaltyPortion != null) {
if (totalAccPenalty == null) { if (totalAccPenalty == null) {
totalAccPenalty = BigDecimal.ZERO; totalAccPenalty = BigDecimal.ZERO;
} }
penaltyportion = penaltyportion.subtract(totalAccPenalty); penaltyPortion = penaltyPortion.subtract(totalAccPenalty);
amount = amount.add(penaltyportion); amount = amount.add(penaltyPortion);
totalAccPenalty = totalAccPenalty.add(penaltyportion); totalAccPenalty = totalAccPenalty.add(penaltyPortion);
if (penaltyportion.compareTo(BigDecimal.ZERO) == 0) { if (penaltyPortion.compareTo(BigDecimal.ZERO) == 0) {
penaltyportion = null; penaltyPortion = null;
} }
} }
if (amount.compareTo(BigDecimal.ZERO) > 0) { if (amount.compareTo(BigDecimal.ZERO) > 0) {
addAccrualAccounting(accrualData, amount, interestportion, totalAccInterest, feeportion, totalAccFee, penaltyportion, addAccrualAccounting(accrualData, amount, interestPortion, totalAccInterest, feePortion, totalAccFee, penaltyPortion,
totalAccPenalty, tilldate); totalAccPenalty, tillDate);
} }
} }
@Transactional @Transactional
public void addAccrualAccounting(LoanScheduleAccrualData scheduleAccrualData) throws Exception { public void addAccrualAccounting(LoanScheduleAccrualData scheduleAccrualData) {
BigDecimal amount = BigDecimal.ZERO; BigDecimal amount = BigDecimal.ZERO;
BigDecimal interestportion = null; BigDecimal interestPortion = null;
BigDecimal totalAccInterest = null; BigDecimal totalAccInterest = null;
if (scheduleAccrualData.getAccruableIncome() != null) { if (scheduleAccrualData.getAccruableIncome() != null) {
interestportion = scheduleAccrualData.getAccruableIncome(); interestPortion = scheduleAccrualData.getAccruableIncome();
totalAccInterest = interestportion; totalAccInterest = interestPortion;
if (scheduleAccrualData.getAccruedInterestIncome() != null) { if (scheduleAccrualData.getAccruedInterestIncome() != null) {
interestportion = interestportion.subtract(scheduleAccrualData.getAccruedInterestIncome()); interestPortion = interestPortion.subtract(scheduleAccrualData.getAccruedInterestIncome());
} }
amount = amount.add(interestportion); amount = amount.add(interestPortion);
if (interestportion.compareTo(BigDecimal.ZERO) == 0) { if (interestPortion.compareTo(BigDecimal.ZERO) == 0) {
interestportion = null; interestPortion = null;
} }
} }
BigDecimal feeportion = null; BigDecimal feePortion = null;
BigDecimal totalAccFee = null; BigDecimal totalAccFee = null;
if (scheduleAccrualData.getDueDateFeeIncome() != null) { if (scheduleAccrualData.getDueDateFeeIncome() != null) {
feeportion = scheduleAccrualData.getDueDateFeeIncome(); feePortion = scheduleAccrualData.getDueDateFeeIncome();
totalAccFee = feeportion; totalAccFee = feePortion;
if (scheduleAccrualData.getAccruedFeeIncome() != null) { if (scheduleAccrualData.getAccruedFeeIncome() != null) {
feeportion = feeportion.subtract(scheduleAccrualData.getAccruedFeeIncome()); feePortion = feePortion.subtract(scheduleAccrualData.getAccruedFeeIncome());
} }
amount = amount.add(feeportion); amount = amount.add(feePortion);
if (feeportion.compareTo(BigDecimal.ZERO) == 0) { if (feePortion.compareTo(BigDecimal.ZERO) == 0) {
feeportion = null; feePortion = null;
} }
} }
BigDecimal penaltyportion = null; BigDecimal penaltyPortion = null;
BigDecimal totalAccPenalty = null; BigDecimal totalAccPenalty = null;
if (scheduleAccrualData.getDueDatePenaltyIncome() != null) { if (scheduleAccrualData.getDueDatePenaltyIncome() != null) {
penaltyportion = scheduleAccrualData.getDueDatePenaltyIncome(); penaltyPortion = scheduleAccrualData.getDueDatePenaltyIncome();
totalAccPenalty = penaltyportion; totalAccPenalty = penaltyPortion;
if (scheduleAccrualData.getAccruedPenaltyIncome() != null) { if (scheduleAccrualData.getAccruedPenaltyIncome() != null) {
penaltyportion = penaltyportion.subtract(scheduleAccrualData.getAccruedPenaltyIncome()); penaltyPortion = penaltyPortion.subtract(scheduleAccrualData.getAccruedPenaltyIncome());
} }
amount = amount.add(penaltyportion); amount = amount.add(penaltyPortion);
if (penaltyportion.compareTo(BigDecimal.ZERO) == 0) { if (penaltyPortion.compareTo(BigDecimal.ZERO) == 0) {
penaltyportion = null; penaltyPortion = null;
} }
} }
if (amount.compareTo(BigDecimal.ZERO) > 0) { if (amount.compareTo(BigDecimal.ZERO) > 0) {
addAccrualAccounting(scheduleAccrualData, amount, interestportion, totalAccInterest, feeportion, totalAccFee, penaltyportion, addAccrualAccounting(scheduleAccrualData, amount, interestPortion, totalAccInterest, feePortion, totalAccFee, penaltyPortion,
totalAccPenalty, scheduleAccrualData.getDueDateAsLocaldate()); totalAccPenalty, scheduleAccrualData.getDueDateAsLocaldate());
} }
} }
private void addAccrualAccounting(LoanScheduleAccrualData scheduleAccrualData, BigDecimal amount, BigDecimal interestportion, private void addAccrualAccounting(LoanScheduleAccrualData scheduleAccrualData, BigDecimal amount, BigDecimal interestPortion,
BigDecimal totalAccInterest, BigDecimal feeportion, BigDecimal totalAccFee, BigDecimal penaltyportion, BigDecimal totalAccInterest, BigDecimal feePortion, BigDecimal totalAccFee, BigDecimal penaltyPortion,
BigDecimal totalAccPenalty, final LocalDate accruedTill) throws DataAccessException { BigDecimal totalAccPenalty, final LocalDate accruedTill) throws DataAccessException {
AppUser user = context.authenticatedUser();
String transactionSql = "INSERT INTO m_loan_transaction (loan_id,office_id,is_reversed,transaction_type_enum,transaction_date,amount,interest_portion_derived," String transactionSql = "INSERT INTO m_loan_transaction (loan_id,office_id,is_reversed,transaction_type_enum,transaction_date,amount,interest_portion_derived,"
+ "fee_charges_portion_derived,penalty_charges_portion_derived, submitted_on_date) VALUES (?, ?, false, ?, ?, ?, ?, ?, ?, ?)"; + "fee_charges_portion_derived,penalty_charges_portion_derived, submitted_on_date, created_by, last_modified_by, created_on_utc, last_modified_on_utc) "
+ "VALUES (?, ?, false, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
this.jdbcTemplate.update(transactionSql, scheduleAccrualData.getLoanId(), scheduleAccrualData.getOfficeId(), this.jdbcTemplate.update(transactionSql, scheduleAccrualData.getLoanId(), scheduleAccrualData.getOfficeId(),
LoanTransactionType.ACCRUAL.getValue(), accruedTill, amount, interestportion, feeportion, penaltyportion, LoanTransactionType.ACCRUAL.getValue(), accruedTill, amount, interestPortion, feePortion, penaltyPortion,
DateUtils.getBusinessLocalDate()); DateUtils.getBusinessLocalDate(), user.getId(), user.getId(), DateUtils.getOffsetDateTimeOfTenant(),
@SuppressWarnings("deprecation") DateUtils.getOffsetDateTimeOfTenant());
final Long transactonId = this.jdbcTemplate.queryForObject("SELECT " + sqlGenerator.lastInsertId(), Long.class); // NOSONAR final Long transactionId = this.jdbcTemplate.queryForObject("SELECT " + sqlGenerator.lastInsertId(), Long.class); // NOSONAR
Map<LoanChargeData, BigDecimal> applicableCharges = scheduleAccrualData.getApplicableCharges(); Map<LoanChargeData, BigDecimal> applicableCharges = scheduleAccrualData.getApplicableCharges();
String chargespaidSql = "INSERT INTO m_loan_charge_paid_by (loan_transaction_id, loan_charge_id, amount,installment_number) VALUES (?,?,?,?)"; String chargesPaidSql = "INSERT INTO m_loan_charge_paid_by (loan_transaction_id, loan_charge_id, amount,installment_number) VALUES (?,?,?,?)";
for (Map.Entry<LoanChargeData, BigDecimal> entry : applicableCharges.entrySet()) { for (Map.Entry<LoanChargeData, BigDecimal> entry : applicableCharges.entrySet()) {
LoanChargeData chargeData = entry.getKey(); LoanChargeData chargeData = entry.getKey();
this.jdbcTemplate.update(chargespaidSql, transactonId, chargeData.getId(), entry.getValue(), this.jdbcTemplate.update(chargesPaidSql, transactionId, chargeData.getId(), entry.getValue(),
scheduleAccrualData.getInstallmentNumber()); scheduleAccrualData.getInstallmentNumber());
} }
Map<String, Object> transactionMap = toMapData(transactonId, amount, interestportion, feeportion, penaltyportion, Map<String, Object> transactionMap = toMapData(transactionId, amount, interestPortion, feePortion, penaltyPortion,
scheduleAccrualData, accruedTill); scheduleAccrualData, accruedTill);
String repaymetUpdatesql = "UPDATE m_loan_repayment_schedule SET accrual_interest_derived=?, accrual_fee_charges_derived=?, " String repaymentUpdateSql = "UPDATE m_loan_repayment_schedule SET accrual_interest_derived=?, accrual_fee_charges_derived=?, "
+ "accrual_penalty_charges_derived=? WHERE id=?"; + "accrual_penalty_charges_derived=? WHERE id=?";
this.jdbcTemplate.update(repaymetUpdatesql, totalAccInterest, totalAccFee, totalAccPenalty, this.jdbcTemplate.update(repaymentUpdateSql, totalAccInterest, totalAccFee, totalAccPenalty,
scheduleAccrualData.getRepaymentScheduleId()); scheduleAccrualData.getRepaymentScheduleId());
String updateLoan = "UPDATE m_loan SET accrued_till=? WHERE id=?"; String updateLoan = "UPDATE m_loan SET accrued_till=?, last_modified_by=?, last_modified_on_utc=? WHERE id=?";
this.jdbcTemplate.update(updateLoan, accruedTill, scheduleAccrualData.getLoanId()); this.jdbcTemplate.update(updateLoan, accruedTill, user.getId(), DateUtils.getOffsetDateTimeOfTenant(),
scheduleAccrualData.getLoanId());
final Map<String, Object> accountingBridgeData = deriveAccountingBridgeData(scheduleAccrualData, transactionMap); final Map<String, Object> accountingBridgeData = deriveAccountingBridgeData(scheduleAccrualData, transactionMap);
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
} }
@@ -301,9 +302,9 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
return accountingBridgeData; return accountingBridgeData;
} }
public Map<String, Object> toMapData(final Long id, final BigDecimal amount, final BigDecimal interestportion, public Map<String, Object> toMapData(final Long id, final BigDecimal amount, final BigDecimal interestPortion,
final BigDecimal feeportion, final BigDecimal penaltyportion, final LoanScheduleAccrualData loanScheduleAccrualData, final BigDecimal feePortion, final BigDecimal penaltyPortion, final LoanScheduleAccrualData loanScheduleAccrualData,
final LocalDate accruredTill) { final LocalDate accruedTill) {
final Map<String, Object> thisTransactionData = new LinkedHashMap<>(); final Map<String, Object> thisTransactionData = new LinkedHashMap<>();
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.ACCRUAL); final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.ACCRUAL);
@@ -312,13 +313,13 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
thisTransactionData.put("officeId", loanScheduleAccrualData.getOfficeId()); thisTransactionData.put("officeId", loanScheduleAccrualData.getOfficeId());
thisTransactionData.put("type", transactionType); thisTransactionData.put("type", transactionType);
thisTransactionData.put("reversed", false); thisTransactionData.put("reversed", false);
thisTransactionData.put("date", accruredTill); thisTransactionData.put("date", accruedTill);
thisTransactionData.put("currency", loanScheduleAccrualData.getCurrencyData()); thisTransactionData.put("currency", loanScheduleAccrualData.getCurrencyData());
thisTransactionData.put("amount", amount); thisTransactionData.put("amount", amount);
thisTransactionData.put("principalPortion", null); thisTransactionData.put("principalPortion", null);
thisTransactionData.put("interestPortion", interestportion); thisTransactionData.put("interestPortion", interestPortion);
thisTransactionData.put("feeChargesPortion", feeportion); thisTransactionData.put("feeChargesPortion", feePortion);
thisTransactionData.put("penaltyChargesPortion", penaltyportion); thisTransactionData.put("penaltyChargesPortion", penaltyPortion);
thisTransactionData.put("overPaymentPortion", null); thisTransactionData.put("overPaymentPortion", null);
Map<LoanChargeData, BigDecimal> applicableCharges = loanScheduleAccrualData.getApplicableCharges(); Map<LoanChargeData, BigDecimal> applicableCharges = loanScheduleAccrualData.getApplicableCharges();
@@ -411,8 +412,8 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
} }
private void updateInterestIncome(final LoanScheduleAccrualData accrualData, private void updateInterestIncome(final LoanScheduleAccrualData accrualData,
final Collection<LoanTransactionData> loanWaiverTansactions, final Collection<LoanSchedulePeriodData> loanSchedulePeriodDatas, final Collection<LoanTransactionData> loanWaiverTransactions,
final LocalDate tilldate) { final Collection<LoanSchedulePeriodData> loanSchedulePeriodDataList, final LocalDate tillDate) {
BigDecimal interestIncome = BigDecimal.ZERO; BigDecimal interestIncome = BigDecimal.ZERO;
if (accrualData.getInterestIncome() != null) { if (accrualData.getInterestIncome() != null) {
@@ -424,17 +425,17 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
BigDecimal remainingAmt = BigDecimal.ZERO; BigDecimal remainingAmt = BigDecimal.ZERO;
Collection<LoanTransactionData> loanTransactionDatas = new ArrayList<>(); Collection<LoanTransactionData> loanTransactionDatas = new ArrayList<>();
for (LoanTransactionData loanTransactionData : loanWaiverTansactions) { for (LoanTransactionData loanTransactionData : loanWaiverTransactions) {
if (!loanTransactionData.dateOf().isAfter(accrualData.getFromDateAsLocaldate()) if (!loanTransactionData.dateOf().isAfter(accrualData.getFromDateAsLocaldate())
|| (loanTransactionData.dateOf().isAfter(accrualData.getFromDateAsLocaldate()) || (loanTransactionData.dateOf().isAfter(accrualData.getFromDateAsLocaldate())
&& !loanTransactionData.dateOf().isAfter(accrualData.getDueDateAsLocaldate()) && !loanTransactionData.dateOf().isAfter(accrualData.getDueDateAsLocaldate())
&& !loanTransactionData.dateOf().isAfter(tilldate))) { && !loanTransactionData.dateOf().isAfter(tillDate))) {
loanTransactionDatas.add(loanTransactionData); loanTransactionDatas.add(loanTransactionData);
} }
} }
Iterator<LoanTransactionData> iterator = loanTransactionDatas.iterator(); Iterator<LoanTransactionData> iterator = loanTransactionDatas.iterator();
for (LoanSchedulePeriodData loanSchedulePeriodData : loanSchedulePeriodDatas) { for (LoanSchedulePeriodData loanSchedulePeriodData : loanSchedulePeriodDataList) {
if (recognized.compareTo(BigDecimal.ZERO) <= 0 && unrecognized.compareTo(BigDecimal.ZERO) <= 0 && iterator.hasNext()) { if (recognized.compareTo(BigDecimal.ZERO) <= 0 && unrecognized.compareTo(BigDecimal.ZERO) <= 0 && iterator.hasNext()) {
LoanTransactionData loanTransactionData = iterator.next(); LoanTransactionData loanTransactionData = iterator.next();
recognized = recognized.add(loanTransactionData.getInterestPortion()); recognized = recognized.add(loanTransactionData.getInterestPortion());
@@ -42,4 +42,6 @@
<include file="parts/0020_add_audit_entries.xml" relativeToChangelogFile="true"/> <include file="parts/0020_add_audit_entries.xml" relativeToChangelogFile="true"/>
<include file="parts/0021_add_spring_batch_db_structure.xml" relativeToChangelogFile="true"/> <include file="parts/0021_add_spring_batch_db_structure.xml" relativeToChangelogFile="true"/>
<include file="parts/0022_add_batch_business_step_configuration_table.xml" relativeToChangelogFile="true"/> <include file="parts/0022_add_batch_business_step_configuration_table.xml" relativeToChangelogFile="true"/>
<include file="parts/0023_use_the_proper_date_or_datetime_type.xml" relativeToChangelogFile="true"/>
<include file="parts/0024_add_audit_entries.xml" relativeToChangelogFile="true"/>
</databaseChangeLog> </databaseChangeLog>
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
<changeSet author="fineract" id="1">
<modifyDataType tableName="m_client_non_person" columnName="incorp_validity_till" newDataType="date"/>
<modifyDataType tableName="m_floating_rates_periods" columnName="from_date" newDataType="date"/>
<modifyDataType tableName="m_holiday" columnName="from_date" newDataType="date"/>
<modifyDataType tableName="m_holiday" columnName="to_date" newDataType="date"/>
<modifyDataType tableName="m_holiday" columnName="repayments_rescheduled_to" newDataType="date"/>
<modifyDataType tableName="m_loan_disbursement_detail" columnName="expected_disburse_date" newDataType="date"/>
<modifyDataType tableName="m_loan_disbursement_detail" columnName="disbursedon_date" newDataType="date"/>
<modifyDataType tableName="m_share_product" columnName="start_date" newDataType="date"/>
<modifyDataType tableName="m_share_product" columnName="end_date" newDataType="date"/>
<modifyDataType tableName="m_surveys" columnName="valid_from" newDataType="date"/>
<modifyDataType tableName="m_surveys" columnName="valid_to" newDataType="date"/>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,241 @@
<?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">
<!--Client-->
<changeSet id="client-4" author="fineract" context="mysql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_client</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_client" columnName="created_on_utc" columnDataType="DATETIME"/>
<addNotNullConstraint tableName="m_client" columnName="created_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_client" columnName="last_modified_on_utc" columnDataType="DATETIME"/>
<addNotNullConstraint tableName="m_client" columnName="last_modified_by" columnDataType="BIGINT"/>
</changeSet>
<changeSet id="client-4" author="fineract" context="postgresql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_client</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_client" columnName="created_on_utc" columnDataType="TIMESTAMP WITH TIME ZONE"/>
<addNotNullConstraint tableName="m_client" columnName="created_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_client" columnName="last_modified_on_utc" columnDataType="TIMESTAMP WITH TIME ZONE"/>
<addNotNullConstraint tableName="m_client" columnName="last_modified_by" columnDataType="BIGINT"/>
</changeSet>
<!--Loan-->
<changeSet id="loan-7" author="fineract" context="mysql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_loan</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_loan" columnName="created_on_utc" columnDataType="DATETIME"/>
<addNotNullConstraint tableName="m_loan" columnName="created_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_loan" columnName="last_modified_on_utc" columnDataType="DATETIME"/>
<addNotNullConstraint tableName="m_loan" columnName="last_modified_by" columnDataType="BIGINT"/>
</changeSet>
<changeSet id="loan-7" author="fineract" context="postgresql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_loan</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_loan" columnName="created_on_utc" columnDataType="TIMESTAMP WITH TIME ZONE"/>
<addNotNullConstraint tableName="m_loan" columnName="created_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_loan" columnName="last_modified_on_utc" columnDataType="TIMESTAMP WITH TIME ZONE"/>
<addNotNullConstraint tableName="m_loan" columnName="last_modified_by" columnDataType="BIGINT"/>
</changeSet>
<!-- Loan transactions -->
<changeSet id="loan-transaction-13" author="fineract" context="mysql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_loan_transaction</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_loan_transaction" columnName="created_on_utc" columnDataType="DATETIME"/>
<addNotNullConstraint tableName="m_loan_transaction" columnName="created_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_loan_transaction" columnName="last_modified_on_utc" columnDataType="DATETIME"/>
<addNotNullConstraint tableName="m_loan_transaction" columnName="last_modified_by" columnDataType="BIGINT"/>
</changeSet>
<changeSet id="loan-transaction-13" author="fineract" context="postgresql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_loan_transaction</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_loan_transaction" columnName="created_on_utc" columnDataType="TIMESTAMP WITH TIME ZONE"/>
<addNotNullConstraint tableName="m_loan_transaction" columnName="created_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_loan_transaction" columnName="last_modified_on_utc" columnDataType="TIMESTAMP WITH TIME ZONE"/>
<addNotNullConstraint tableName="m_loan_transaction" columnName="last_modified_by" columnDataType="BIGINT"/>
</changeSet>
<!-- Client transaction -->
<changeSet author="fineract" id="client-transaction-1" context="mysql">
<addColumn tableName="m_client_transaction">
<column name="created_on_utc" type="DATETIME"/>
<column name="created_by" type="BIGINT" valueComputed="appuser_id"/>
<column name="last_modified_by" type="BIGINT"/>
<column name="last_modified_on_utc" type="DATETIME"/>
</addColumn>
</changeSet>
<changeSet author="fineract" id="client-transaction-1" context="postgresql">
<addColumn tableName="m_client_transaction">
<column name="created_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
<column name="created_by" type="BIGINT" valueComputed="appuser_id"/>
<column name="last_modified_by" type="BIGINT"/>
<column name="last_modified_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
</addColumn>
</changeSet>
<changeSet id="client-transaction-2" author="fineract">
<addColumn tableName="m_client_transaction">
<column name="submitted_on_date" type="DATE" valueComputed="created_date">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
<changeSet author="fineract" id="client-transaction-3">
<addForeignKeyConstraint baseColumnNames="created_by" baseTableName="m_client_transaction"
constraintName="FK_client_transaction_created_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_appuser" validate="true"/>
<addForeignKeyConstraint baseColumnNames="last_modified_by" baseTableName="m_client_transaction"
constraintName="FK_client_transaction_last_modified_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_appuser" validate="true"/>
</changeSet>
<changeSet id="client-transaction-4" author="fineract">
<dropForeignKeyConstraint baseTableName="m_client_transaction" constraintName="FK_m_client_transaction_m_appuser"/>
<dropColumn tableName="m_client_transaction">
<column name="appuser_id"/>
</dropColumn>
</changeSet>
<changeSet id="client-transaction-5" author="fineract" context="mysql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_client_transaction</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_client_transaction" columnName="created_on_utc" columnDataType="DATETIME"/>
<addNotNullConstraint tableName="m_client_transaction" columnName="created_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_client_transaction" columnName="last_modified_on_utc" columnDataType="DATETIME"/>
<addNotNullConstraint tableName="m_client_transaction" columnName="last_modified_by" columnDataType="BIGINT"/>
</changeSet>
<changeSet id="client-transaction-5" author="fineract" context="postgresql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_client_transaction</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_client_transaction" columnName="created_on_utc" columnDataType="TIMESTAMP WITH TIME ZONE"/>
<addNotNullConstraint tableName="m_client_transaction" columnName="created_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_client_transaction" columnName="last_modified_on_utc" columnDataType="TIMESTAMP WITH TIME ZONE"/>
<addNotNullConstraint tableName="m_client_transaction" columnName="last_modified_by" columnDataType="BIGINT"/>
</changeSet>
<changeSet id="client-transaction-6" author="fineract">
<dropNotNullConstraint tableName="m_client_transaction" columnName="created_date" columnDataType="datetime"/>
</changeSet>
<!-- Floating rate -->
<changeSet author="fineract" id="floating-rate-1" context="mysql">
<addColumn tableName="m_floating_rates">
<column name="created_on_utc" type="DATETIME"/>
<column name="last_modified_on_utc" type="DATETIME"/>
</addColumn>
</changeSet>
<changeSet author="fineract" id="floating-rate-1" context="postgresql">
<addColumn tableName="m_floating_rates">
<column name="created_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
<column name="last_modified_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
</addColumn>
</changeSet>
<changeSet id="floating-rate-2" author="fineract">
<renameColumn tableName="m_floating_rates" oldColumnName="createdby_id" newColumnName="created_by" columnDataType="BIGINT"/>
<renameColumn tableName="m_floating_rates" oldColumnName="lastmodifiedby_id" newColumnName="last_modified_by" columnDataType="BIGINT"/>
</changeSet>
<changeSet author="fineract" id="floating-rate-3">
<addForeignKeyConstraint baseColumnNames="created_by" baseTableName="m_floating_rates"
constraintName="FK_floating_rates_created_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_appuser" validate="true"/>
<addForeignKeyConstraint baseColumnNames="last_modified_by" baseTableName="m_floating_rates"
constraintName="FK_floating_rates_last_modified_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_appuser" validate="true"/>
</changeSet>
<changeSet id="floating-rate-4" author="fineract" context="mysql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_floating_rates</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_floating_rates" columnName="created_on_utc" columnDataType="DATETIME"/>
<addNotNullConstraint tableName="m_floating_rates" columnName="created_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_floating_rates" columnName="last_modified_on_utc" columnDataType="DATETIME"/>
<addNotNullConstraint tableName="m_floating_rates" columnName="last_modified_by" columnDataType="BIGINT"/>
</changeSet>
<changeSet id="floating-rate-4" author="fineract" context="postgresql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_client_transaction</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_floating_rates" columnName="created_on_utc" columnDataType="TIMESTAMP WITH TIME ZONE"/>
<addNotNullConstraint tableName="m_floating_rates" columnName="created_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_floating_rates" columnName="last_modified_on_utc" columnDataType="TIMESTAMP WITH TIME ZONE"/>
<addNotNullConstraint tableName="m_floating_rates" columnName="last_modified_by" columnDataType="BIGINT"/>
</changeSet>
<changeSet id="floating-rate-5" author="fineract">
<dropNotNullConstraint tableName="m_floating_rates" columnName="created_date" columnDataType="datetime"/>
<dropNotNullConstraint tableName="m_floating_rates" columnName="lastmodified_date" columnDataType="datetime"/>
</changeSet>
<!-- Floating rates period -->
<changeSet author="fineract" id="floating-rate-period-1" context="mysql">
<addColumn tableName="m_floating_rates_periods">
<column name="created_on_utc" type="DATETIME"/>
<column name="last_modified_on_utc" type="DATETIME"/>
</addColumn>
</changeSet>
<changeSet author="fineract" id="floating-rate-period-1" context="postgresql">
<addColumn tableName="m_floating_rates_periods">
<column name="created_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
<column name="last_modified_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
</addColumn>
</changeSet>
<changeSet id="floating-rate-period-2" author="fineract">
<renameColumn tableName="m_floating_rates_periods" oldColumnName="createdby_id" newColumnName="created_by" columnDataType="BIGINT"/>
<renameColumn tableName="m_floating_rates_periods" oldColumnName="lastmodifiedby_id" newColumnName="last_modified_by" columnDataType="BIGINT"/>
</changeSet>
<changeSet author="fineract" id="floating-rate-period-3">
<addForeignKeyConstraint baseColumnNames="created_by" baseTableName="m_floating_rates_periods"
constraintName="FK_floating_rates_period_created_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_appuser" validate="true"/>
<addForeignKeyConstraint baseColumnNames="last_modified_by" baseTableName="m_floating_rates_periods"
constraintName="FK_floating_rates_period_last_modified_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_appuser" validate="true"/>
</changeSet>
<changeSet id="floating-rate-period-4" author="fineract" context="mysql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_floating_rates_periods</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_floating_rates_periods" columnName="created_on_utc" columnDataType="DATETIME"/>
<addNotNullConstraint tableName="m_floating_rates_periods" columnName="created_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_floating_rates_periods" columnName="last_modified_on_utc" columnDataType="DATETIME"/>
<addNotNullConstraint tableName="m_floating_rates_periods" columnName="last_modified_by" columnDataType="BIGINT"/>
</changeSet>
<changeSet id="floating-rate-period-4" author="fineract" context="postgresql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_floating_rates_periods</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_floating_rates_periods" columnName="created_on_utc" columnDataType="TIMESTAMP WITH TIME ZONE"/>
<addNotNullConstraint tableName="m_floating_rates_periods" columnName="created_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_floating_rates_periods" columnName="last_modified_on_utc" columnDataType="TIMESTAMP WITH TIME ZONE"/>
<addNotNullConstraint tableName="m_floating_rates_periods" columnName="last_modified_by" columnDataType="BIGINT"/>
</changeSet>
<changeSet id="floating-rate-period-5" author="fineract">
<dropNotNullConstraint tableName="m_floating_rates_periods" columnName="created_date" columnDataType="datetime"/>
<dropNotNullConstraint tableName="m_floating_rates_periods" columnName="lastmodified_date" columnDataType="datetime"/>
</changeSet>
</databaseChangeLog>
@@ -30,7 +30,10 @@ import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -442,7 +445,7 @@ public class FixedDepositTest {
} }
@Test @Test
public void testFixedDepositAccountClosureTypeWithdrawal_WITH_HOLD_TAX() throws InterruptedException { public void testFixedDepositAccountClosureTypeWithdrawal_WITH_HOLD_TAX() {
this.fixedDepositProductHelper = new FixedDepositProductHelper(this.requestSpec, this.responseSpec); this.fixedDepositProductHelper = new FixedDepositProductHelper(this.requestSpec, this.responseSpec);
this.accountHelper = new AccountHelper(this.requestSpec, this.responseSpec); this.accountHelper = new AccountHelper(this.requestSpec, this.responseSpec);
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec); this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
@@ -457,30 +460,23 @@ public class FixedDepositTest {
final Account liabilityAccount = this.accountHelper.createLiabilityAccount(); final Account liabilityAccount = this.accountHelper.createLiabilityAccount();
final Account liabilityAccountForTax = this.accountHelper.createLiabilityAccount(); final Account liabilityAccountForTax = this.accountHelper.createLiabilityAccount();
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy", Locale.US); DateTimeFormatter monthDayFormat = new DateTimeFormatterBuilder().appendPattern("dd MMM").toFormatter();
DateFormat monthDayFormat = new SimpleDateFormat("dd MMM", Locale.US); DateTimeFormatter currentDateFormat = new DateTimeFormatterBuilder().appendPattern("dd").toFormatter();
DateFormat currentDateFormat = new SimpleDateFormat("dd");
Calendar todaysDate = Calendar.getInstance(); LocalDate todaysDate = Utils.getLocalDateOfTenant();
todaysDate.add(Calendar.MONTH, -20); todaysDate = todaysDate.minusMonths(20);
final String VALID_FROM = dateFormat.format(todaysDate.getTime()); final String VALID_FROM = Utils.dateFormatter.format(todaysDate);
todaysDate.add(Calendar.YEAR, 10); todaysDate = todaysDate.plusYears(10);
final String VALID_TO = dateFormat.format(todaysDate.getTime()); final String VALID_TO = Utils.dateFormatter.format(todaysDate);
todaysDate = Calendar.getInstance(); todaysDate = Utils.getLocalDateOfTenant();
todaysDate.add(Calendar.MONTH, -20); todaysDate = todaysDate.minusMonths(20);
final String SUBMITTED_ON_DATE = dateFormat.format(todaysDate.getTime()); final String SUBMITTED_ON_DATE = Utils.dateFormatter.format(todaysDate);
final String APPROVED_ON_DATE = dateFormat.format(todaysDate.getTime()); final String APPROVED_ON_DATE = Utils.dateFormatter.format(todaysDate);
final String ACTIVATION_DATE = dateFormat.format(todaysDate.getTime()); final String ACTIVATION_DATE = Utils.dateFormatter.format(todaysDate);
monthDayFormat.format(todaysDate.getTime());
Integer currentDate = Integer.valueOf(currentDateFormat.format(todaysDate.getTime())); LocalDate closedOn = todaysDate.plusMonths(14);
Integer daysInMonth = todaysDate.getActualMaximum(Calendar.DATE); final String CLOSED_ON_DATE = Utils.dateFormatter.format(closedOn);
int numberOfDaysLeft = daysInMonth - currentDate + 1;
todaysDate.add(Calendar.DATE, numberOfDaysLeft);
Calendar closedOn = Calendar.getInstance();
closedOn.add(Calendar.MONTH, -6);
final String CLOSED_ON_DATE = dateFormat.format(closedOn.getTime());
Integer clientId = ClientHelper.createClient(requestSpec, responseSpec); Integer clientId = ClientHelper.createClient(requestSpec, responseSpec);
Assertions.assertNotNull(clientId); Assertions.assertNotNull(clientId);
@@ -1282,26 +1278,23 @@ public class FixedDepositTest {
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec); this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
this.fixedDepositAccountHelper = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec); this.fixedDepositAccountHelper = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec);
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy", Locale.US); DateTimeFormatter dateFormat = Utils.dateFormatter;
DateFormat monthDayFormat = new SimpleDateFormat("dd MMM", Locale.US);
DateFormat currentDateFormat = new SimpleDateFormat("dd");
Calendar todaysDate = Calendar.getInstance(); LocalDate todaysDate = Utils.getLocalDateOfTenant();
todaysDate.add(Calendar.MONTH, -3); todaysDate = todaysDate.minusMonths(3);
final String VALID_FROM = dateFormat.format(todaysDate.getTime()); final String VALID_FROM = dateFormat.format(todaysDate);
todaysDate.add(Calendar.YEAR, 10); todaysDate = todaysDate.plusYears(10);
final String VALID_TO = dateFormat.format(todaysDate.getTime()); final String VALID_TO = dateFormat.format(todaysDate);
todaysDate = Calendar.getInstance(); todaysDate = Utils.getLocalDateOfTenant();
todaysDate.add(Calendar.MONTH, -1); todaysDate = todaysDate.minusMonths(1);
todaysDate.add(Calendar.DAY_OF_MONTH, -1); todaysDate = todaysDate.minusDays(1);
final String SUBMITTED_ON_DATE = dateFormat.format(todaysDate.getTime()); final String SUBMITTED_ON_DATE = dateFormat.format(todaysDate);
final String APPROVED_ON_DATE = dateFormat.format(todaysDate.getTime()); final String APPROVED_ON_DATE = dateFormat.format(todaysDate);
final String ACTIVATION_DATE = dateFormat.format(todaysDate.getTime()); final String ACTIVATION_DATE = dateFormat.format(todaysDate);
monthDayFormat.format(todaysDate.getTime());
todaysDate.add(Calendar.MONTH, 1); todaysDate = Utils.getLocalDateOfTenant();
todaysDate.add(Calendar.DAY_OF_MONTH, 1); final String CLOSED_ON_DATE = dateFormat.format(todaysDate);
final String CLOSED_ON_DATE = dateFormat.format(todaysDate.getTime());
Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec); Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec);
Assertions.assertNotNull(clientId); Assertions.assertNotNull(clientId);
@@ -1345,18 +1338,13 @@ public class FixedDepositTest {
log.info("per day = {}", perDay); log.info("per day = {}", perDay);
double interestPerDay = interestRateInFraction * perDay; double interestPerDay = interestRateInFraction * perDay;
todaysDate.add(Calendar.MONTH, -1); todaysDate = todaysDate.minusMonths(1);
todaysDate.add(Calendar.DAY_OF_MONTH, -1); todaysDate = todaysDate.minusDays(1);
Integer currentDate = Integer.valueOf(currentDateFormat.format(todaysDate.getTime()));
Integer daysInMonth = todaysDate.getActualMaximum(Calendar.DATE); Float interestPerMonth = (float) (interestPerDay * principal * ChronoUnit.DAYS.between(todaysDate, Utils.getLocalDateOfTenant()));
daysInMonth = daysInMonth - currentDate + 1;
Float interestPerMonth = (float) (interestPerDay * principal * daysInMonth);
principal += interestPerMonth; principal += interestPerMonth;
todaysDate.add(Calendar.DATE, daysInMonth); log.info("{}", Utils.dateFormatter.format(todaysDate));
log.info("{}", monthDayFormat.format(todaysDate.getTime()));
interestPerMonth = (float) (interestPerDay * principal * currentDate);
log.info("IPM = {}", interestPerMonth); log.info("IPM = {}", interestPerMonth);
principal += interestPerMonth;
log.info("principal = {}", principal); log.info("principal = {}", principal);
Integer transactionIdForPostInterest = this.fixedDepositAccountHelper.postInterestForFixedDeposit(fixedDepositAccountId); Integer transactionIdForPostInterest = this.fixedDepositAccountHelper.postInterestForFixedDeposit(fixedDepositAccountId);