diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 693455c95..2941a8bf7 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -11123,6 +11123,21 @@ GET https://DomainName/api/v1/charges/template "id": 2, "code": "chargeTimeType.specifiedDueDate", "value": "Specified due date" + }, + { + "id": 3, + "code": "chargeTimeType.savingsActivation", + "value": "Savings Activation" + }, + { + "id": 5, + "code": "chargeTimeType.withdrawalFee", + "value": "Withdrawal Fee" + }, + { + "id": 6, + "code": "chargeTimeType.annualFee", + "value": "Annual Fee" } ], "chargePaymetModeOptions": diff --git a/mifosng-db/migrations/core_db/V118_7__move_withdrawal_annual_fee_to_charges.sql b/mifosng-db/migrations/core_db/V118_7__move_withdrawal_annual_fee_to_charges.sql index 791f391ef..21312590f 100644 --- a/mifosng-db/migrations/core_db/V118_7__move_withdrawal_annual_fee_to_charges.sql +++ b/mifosng-db/migrations/core_db/V118_7__move_withdrawal_annual_fee_to_charges.sql @@ -1,25 +1,253 @@ ALTER TABLE `m_savings_account_charge` + CHANGE COLUMN `due_for_collection_as_of_date` `charge_due_date` DATE NULL DEFAULT NULL AFTER `charge_time_enum`; + +ALTER TABLE `m_savings_account_charge` + ADD COLUMN `fee_on_month` SMALLINT(5) NULL DEFAULT NULL AFTER `charge_due_date`, + ADD COLUMN `fee_on_day` SMALLINT(5) NULL DEFAULT NULL AFTER `fee_on_month`, ADD COLUMN `is_active` TINYINT(1) NOT NULL DEFAULT '1' AFTER `waived`; -ALTER TABLE `m_savings_account` - DROP COLUMN `withdrawal_fee_amount`, - DROP COLUMN `withdrawal_fee_type_enum`; -ALTER TABLE `m_savings_account_charge` - ADD COLUMN `fee_on_month` SMALLINT(5) NULL DEFAULT NULL AFTER `due_for_collection_as_of_date`, - ADD COLUMN `fee_on_day` SMALLINT(5) NULL DEFAULT NULL AFTER `fee_on_month`; +delimiter // +CREATE PROCEDURE migrate_withdrwal_fees() +begin + declare no_more_rows boolean default false; + declare v_currency_code VARCHAR(3); + declare v_withdrawal_fee_type_enum SMALLINT(5); + declare v_withdrawal_fee_amount_charge_def DECIMAL(19,6); + declare v_account_id BIGINT(20); + declare v_withdrawal_fee_amount DECIMAL(19,6); + declare t_calculation_percentage DECIMAL(19,6); + declare t_calculation_on_amount DECIMAL(19,6); + declare t_withdrawal_fee_name VARCHAR(100); + + declare t_charge_id BIGINT(20); + declare t_savings_charge_id BIGINT(20); -ALTER TABLE `m_savings_account_charge` - CHANGE COLUMN `due_for_collection_as_of_date` `charge_due_date` DATE NULL DEFAULT NULL AFTER `charge_time_enum`; + -- savings transaction variables + declare v_savings_transaction_id BIGINT(20); + declare v_transaction_amount DECIMAL(19,6); + + + declare cursor1 cursor for + select sa.currency_code, sa.withdrawal_fee_type_enum, sa.withdrawal_fee_amount + from m_savings_account sa where sa.withdrawal_fee_amount is not null and sa.withdrawal_fee_amount > 0 group by sa.currency_code, sa.withdrawal_fee_type_enum; + + declare cursor2 cursor for + select sa.id, sa.withdrawal_fee_amount from m_savings_account sa where sa.currency_code=v_currency_code and sa.withdrawal_fee_type_enum=v_withdrawal_fee_type_enum and sa.withdrawal_fee_amount is not null and sa.withdrawal_fee_amount > 0; + + declare cursor3 cursor for + select sat.id, sat.amount from m_savings_account_transaction sat + where sat.savings_account_id=v_account_id and sat.transaction_type_enum=4; + + declare continue handler for not found + set no_more_rows := true; + + open cursor1; + LOOP1: loop + fetch cursor1 into v_currency_code, v_withdrawal_fee_type_enum, v_withdrawal_fee_amount_charge_def; + if no_more_rows then + close cursor1; + leave LOOP1; + end if; + -- set withdrawal fee name + if(v_withdrawal_fee_type_enum = 1) then + set t_withdrawal_fee_name = CONCAT('Withdrawal fee-Flat-',v_currency_code); + else + set t_withdrawal_fee_name = CONCAT('Withdrawal fee-Percentage-',v_currency_code); + end if; + + -- get charge id if already exists + set t_charge_id = (select id from m_charge where name = t_withdrawal_fee_name); + + if t_charge_id is null then + -- add withdrawal fee to charges + INSERT INTO `m_charge` (`name`, `currency_code`, `charge_applies_to_enum`, `charge_time_enum`, `charge_calculation_enum`, `charge_payment_mode_enum`, `amount`, `is_penalty`, `is_active`, `is_deleted`) VALUES (t_withdrawal_fee_name , v_currency_code, 2, 5, v_withdrawal_fee_type_enum, 0, v_withdrawal_fee_amount_charge_def, 0, 1, 0); + + -- get inserted charge id + set t_charge_id = last_insert_id(); + end if; + + open cursor2; + LOOP2: loop + fetch cursor2 into v_account_id, v_withdrawal_fee_amount; + if no_more_rows then + set no_more_rows := false; + close cursor2; + leave LOOP2; + end if; + + if (v_withdrawal_fee_type_enum=1) then + set t_calculation_percentage = NULL; + set t_calculation_on_amount = NULL; + else + set t_calculation_percentage = v_withdrawal_fee_amount; + set v_withdrawal_fee_amount = 0; + set t_calculation_on_amount = 0; + end if; + + if not exists (select id from m_savings_account_charge sac where + sac.savings_account_id=v_account_id and sac.charge_id=t_charge_id and sac.charge_time_enum=5) then + + -- attach withdrawal charge to savings + INSERT INTO `m_savings_account_charge` (`savings_account_id`, `charge_id`, `is_penalty`, `charge_time_enum`, `charge_due_date`, `fee_on_month`, `fee_on_day`, `charge_calculation_enum`, `calculation_percentage`, `calculation_on_amount`, `amount`, `amount_paid_derived`, `amount_waived_derived`, `amount_writtenoff_derived`, `amount_outstanding_derived`, `is_paid_derived`, `waived`, `is_active`) VALUES (v_account_id, t_charge_id, 0, 5, NULL, NULL, NULL, v_withdrawal_fee_type_enum, t_calculation_percentage, t_calculation_on_amount, v_withdrawal_fee_amount, NULL, NULL, NULL, 0.000000, 0, 0, 1); + + -- set savings account charge id + set t_savings_charge_id = last_insert_id(); + + else + + set t_savings_charge_id = (select id from m_savings_account_charge sac where sac.savings_account_id=v_account_id and sac.charge_id=t_charge_id and sac.charge_time_enum=5); + + end if; + + + open cursor3; + LOOP3: loop + fetch cursor3 into v_savings_transaction_id, v_transaction_amount; + + if no_more_rows then + set no_more_rows := false; + close cursor3; + leave LOOP3; + end if; + + if not exists (select id from m_savings_account_charge_paid_by sacp where + sacp.savings_account_transaction_id=v_savings_transaction_id and sacp.savings_account_charge_id=t_savings_charge_id) then + + -- insert a record into savings account charge paid by + INSERT INTO `m_savings_account_charge_paid_by` (`savings_account_transaction_id`, `savings_account_charge_id`, `amount`) VALUES(v_savings_transaction_id, t_savings_charge_id, v_transaction_amount); + + end if; + + end loop LOOP3; + end loop LOOP2; + end loop LOOP1; +end // + +CREATE PROCEDURE migrate_annual_fees() +begin + declare no_more_rows boolean default false; + declare v_currency_code VARCHAR(3); + declare v_annual_fee_amount_charge_def DECIMAL(19,6); + declare v_account_id BIGINT(20); + declare v_annual_fee_amount DECIMAL(19,6); + declare v_annual_fee_on_month SMALLINT(5); + declare v_annual_fee_on_day SMALLINT(5); + declare v_annual_fee_next_due_date DATE; + declare t_annual_fee_name VARCHAR(100); + declare t_charge_id BIGINT(20); + declare t_savings_charge_id BIGINT(20); + + -- savings transaction variables + declare v_savings_transaction_id BIGINT(20); + declare v_transaction_amount DECIMAL(19,6); + + + declare cursor1 cursor for + select sa.currency_code, sa.annual_fee_amount + from m_savings_account sa where sa.annual_fee_amount is not null and sa.annual_fee_on_month is not null and sa.annual_fee_on_day is not null group by sa.currency_code; + + declare cursor2 cursor for + select sa.id, sa.annual_fee_amount, sa.annual_fee_on_month, sa.annual_fee_on_day, sa.annual_fee_next_due_date from m_savings_account sa where sa.currency_code=v_currency_code and sa.annual_fee_amount is not null and sa.annual_fee_on_month is not null and sa.annual_fee_on_day is not null; + + declare cursor3 cursor for + select sat.id, sat.amount from m_savings_account_transaction sat + where sat.savings_account_id=v_account_id and sat.transaction_type_enum=5; + + declare continue handler for not found + set no_more_rows := true; + + open cursor1; + LOOP1: loop + fetch cursor1 into v_currency_code, v_annual_fee_amount_charge_def; + + if no_more_rows then + close cursor1; + leave LOOP1; + end if; + + -- set annual fee name + set t_annual_fee_name = CONCAT('Annual fee - ',v_currency_code); + + -- get charge id if already exists + set t_charge_id = (select id from m_charge where name = t_annual_fee_name); + + if t_charge_id is null then + + -- add annual fee to charges + INSERT INTO `m_charge` (`name`, `currency_code`, `charge_applies_to_enum`, `charge_time_enum`, `charge_calculation_enum`, `charge_payment_mode_enum`, `amount`, `is_penalty`, `is_active`, `is_deleted`) VALUES (t_annual_fee_name, v_currency_code, 2, 6, 1, 0, v_annual_fee_amount_charge_def, 0, 1, 0); + + -- get inserted charge id + set t_charge_id = last_insert_id(); + + end if; + + open cursor2; + LOOP2: loop + fetch cursor2 into v_account_id, v_annual_fee_amount, v_annual_fee_on_month, v_annual_fee_on_day, v_annual_fee_next_due_date; + + if no_more_rows then + set no_more_rows := false; + close cursor2; + leave LOOP2; + end if; + + if not exists (select id from m_savings_account_charge sac where + sac.savings_account_id=v_account_id and sac.charge_id=t_charge_id and sac.charge_time_enum=6) then + + -- attach annual charge to savings + INSERT INTO `m_savings_account_charge` (`savings_account_id`, `charge_id`, `is_penalty`, `charge_time_enum`, `charge_due_date`, `fee_on_month`, `fee_on_day`, `charge_calculation_enum`, `calculation_percentage`, `calculation_on_amount`, `amount`, `amount_paid_derived`, `amount_waived_derived`, `amount_writtenoff_derived`, `amount_outstanding_derived`, `is_paid_derived`, `waived`, `is_active`) VALUES (v_account_id, t_charge_id, 0, 6, v_annual_fee_next_due_date, v_annual_fee_on_month, v_annual_fee_on_day, 1, NULL, NULL, v_annual_fee_amount, NULL, NULL, NULL, v_annual_fee_amount, 0, 0, 1); + + -- set savings account charge id + set t_savings_charge_id = last_insert_id(); + + else + + set t_savings_charge_id = (select id from m_savings_account_charge sac where sac.savings_account_id=v_account_id and sac.charge_id=t_charge_id and sac.charge_time_enum=6); + + end if; + + open cursor3; + LOOP3: loop + fetch cursor3 into v_savings_transaction_id, v_transaction_amount; + + if no_more_rows then + set no_more_rows := false; + close cursor3; + leave LOOP3; + end if; + + if not exists (select id from m_savings_account_charge_paid_by sacp where + sacp.savings_account_transaction_id=v_savings_transaction_id and sacp.savings_account_charge_id=t_savings_charge_id) then + + -- insert a record into savings account charge paid by + INSERT INTO `m_savings_account_charge_paid_by` (`savings_account_transaction_id`, `savings_account_charge_id`, `amount`) VALUES(v_savings_transaction_id, t_savings_charge_id, v_transaction_amount); + + end if; + + end loop LOOP3; + end loop LOOP2; + end loop LOOP1; +end // + +delimiter ; + +call migrate_withdrwal_fees(); +call migrate_annual_fees(); + +drop procedure if exists migrate_annual_fees; +drop procedure if exists migrate_withdrwal_fees; ALTER TABLE `m_savings_account` DROP COLUMN `annual_fee_amount`, DROP COLUMN `annual_fee_on_month`, DROP COLUMN `annual_fee_on_day`, - DROP COLUMN `annual_fee_next_due_date`; + DROP COLUMN `annual_fee_next_due_date`, + DROP COLUMN `withdrawal_fee_amount`, + DROP COLUMN `withdrawal_fee_type_enum`; ALTER TABLE `m_savings_product` DROP COLUMN `annual_fee_amount`, DROP COLUMN `annual_fee_on_month`, - DROP COLUMN `annual_fee_on_day`; \ No newline at end of file + DROP COLUMN `annual_fee_on_day`; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/serialization/JsonParserHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/serialization/JsonParserHelper.java index 38c89021c..a7f6dfc40 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/serialization/JsonParserHelper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/serialization/JsonParserHelper.java @@ -293,8 +293,20 @@ public class JsonParserHelper { final JsonPrimitive primitive = object.get(parameterName).getAsJsonPrimitive(); final String valueAsString = primitive.getAsString(); if (StringUtils.isNotBlank(valueAsString)) { - final DateTimeFormatter formatter = DateTimeFormat.forPattern(dateFormat).withLocale(clientApplicationLocale); - value = MonthDay.parse(valueAsString.toLowerCase(clientApplicationLocale), formatter); + try { + final DateTimeFormatter formatter = DateTimeFormat.forPattern(dateFormat).withLocale(clientApplicationLocale); + value = MonthDay.parse(valueAsString.toLowerCase(clientApplicationLocale), formatter); + } catch (final IllegalArgumentException e) { + final List dataValidationErrors = new ArrayList(); + final ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.month.day", + "The parameter " + parameterName + " is invalid based on the dateFormat: '" + dateFormat + + "' and locale: '" + clientApplicationLocale + "' provided:", parameterName, valueAsString, + dateFormat); + dataValidationErrors.add(error); + + throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", + dataValidationErrors); + } } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/data/ChargeData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/data/ChargeData.java index 77de17e18..903438c3d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/data/ChargeData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/data/ChargeData.java @@ -169,6 +169,7 @@ public class ChargeData implements Comparable, Serializable { public SavingsAccountChargeData toSavingsAccountChargeData() { final Long savingsChargeId = null; + final Long savingsAccountId = null; final BigDecimal amountPaid = BigDecimal.ZERO; final BigDecimal amountWaived = BigDecimal.ZERO; final BigDecimal amountWrittenOff = BigDecimal.ZERO; @@ -179,7 +180,7 @@ public class ChargeData implements Comparable, Serializable { final LocalDate dueAsOfDate = null; final MonthDay feeOnMonthDay = null; - return SavingsAccountChargeData.instance(savingsChargeId, this.id, this.name, this.currency, this.amount, amountPaid, amountWaived, + return SavingsAccountChargeData.instance(savingsChargeId, this.id, savingsAccountId, this.name, this.currency, this.amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding, this.chargeTimeType, dueAsOfDate, this.chargeCalculationType, percentage, amountPercentageAppliedTo, chargeOptions, this.penalty, feeOnMonthDay); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/Charge.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/Charge.java index f9859a3be..3ce2887fe 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/Charge.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/Charge.java @@ -117,7 +117,7 @@ public class Charge extends AbstractPersistable { if (!ChargeTimeType.fromInt(getChargeTime()).isWithdrawalFee() && ChargeCalculationType.fromInt(getChargeCalculation()).isPercentageOfAmount()) { baseDataValidator.reset().parameter("chargeCalculationType").value(this.chargeCalculation) - .failWithCodeNoParameterAddedToErrorCode("charge.calculation.type.percentage.allowed.only.for.withdrawal"); + .failWithCodeNoParameterAddedToErrorCode("savings.charge.calculation.type.percentage.allowed.only.for.withdrawal"); } } else if (isLoanCharge()) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeDropdownReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeDropdownReadPlatformServiceImpl.java index 41d761495..23bdbd925 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeDropdownReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeDropdownReadPlatformServiceImpl.java @@ -49,7 +49,7 @@ public class ChargeDropdownReadPlatformServiceImpl implements ChargeDropdownRead public List retrieveCollectionTimeTypes() { final List chargeTimeTypes = new ArrayList(); for (final ChargeTimeType chargeTimeType : ChargeTimeType.values()) { - if (ChargeTimeType.INVALID.equals(chargeTimeType)) { + if (ChargeTimeType.INVALID.equals(chargeTimeType) || ChargeTimeType.SAVINGS_CLOSURE.equals(chargeTimeType)) { continue; } chargeTimeTypes.add(ChargeEnumerations.chargeTimeType(chargeTimeType)); @@ -81,7 +81,8 @@ public class ChargeDropdownReadPlatformServiceImpl implements ChargeDropdownRead @Override public List retrieveSavingsCollectionTimeTypes() { return Arrays.asList(chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE), chargeTimeType(ChargeTimeType.SAVINGS_ACTIVATION), - chargeTimeType(ChargeTimeType.SAVINGS_CLOSURE), chargeTimeType(ChargeTimeType.WITHDRAWAL_FEE), + //chargeTimeType(ChargeTimeType.SAVINGS_CLOSURE), + chargeTimeType(ChargeTimeType.WITHDRAWAL_FEE), chargeTimeType(ChargeTimeType.ANNUAL_FEE)); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java index bdab0a9b9..44b7bb681 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java @@ -164,7 +164,7 @@ public class SavingsApiConstants { interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, //withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, - //withdrawalFeeForTransfersParamName, + withdrawalFeeForTransfersParamName, feeAmountParamName, feeOnMonthDayParamName, chargesParamName)); /** diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountAnnualFeeData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountAnnualFeeData.java index 6aed6e0e9..bb5cc910e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountAnnualFeeData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountAnnualFeeData.java @@ -10,15 +10,17 @@ import org.joda.time.LocalDate; public class SavingsAccountAnnualFeeData { private final Long id; + private final Long accountId; private final String accountNo; private final LocalDate nextAnnualFeeDueDate; - public static SavingsAccountAnnualFeeData instance(final Long id, final String accountNo, final LocalDate nextAnnualFeeDueDate) { - return new SavingsAccountAnnualFeeData(id, accountNo, nextAnnualFeeDueDate); + public static SavingsAccountAnnualFeeData instance(final Long id, final Long accountId, final String accountNo, final LocalDate nextAnnualFeeDueDate) { + return new SavingsAccountAnnualFeeData(id, accountId, accountNo, nextAnnualFeeDueDate); } - private SavingsAccountAnnualFeeData(final Long id, final String accountNo, final LocalDate nextAnnualFeeDueDate) { + private SavingsAccountAnnualFeeData(final Long id, final Long accountId, final String accountNo, final LocalDate nextAnnualFeeDueDate) { this.id = id; + this.accountId = accountId; this.accountNo = accountNo; this.nextAnnualFeeDueDate = nextAnnualFeeDueDate; } @@ -27,11 +29,15 @@ public class SavingsAccountAnnualFeeData { return this.id; } - public String getAccountNo() { - return this.accountNo; + public Long getAccountId() { + return this.accountId; } public LocalDate getNextAnnualFeeDueDate() { return this.nextAnnualFeeDueDate; } + + public String getAccountNo() { + return this.accountNo; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountChargeData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountChargeData.java index 351e37c1f..6622f9241 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountChargeData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountChargeData.java @@ -25,6 +25,9 @@ public class SavingsAccountChargeData { @SuppressWarnings("unused") private final Long chargeId; + + @SuppressWarnings("unused") + private final Long accountId; @SuppressWarnings("unused") private final String name; @@ -73,6 +76,7 @@ public class SavingsAccountChargeData { public static SavingsAccountChargeData template(final Collection chargeOptions) { final Long id = null; final Long chargeId = null; + final Long accountId = null; final String name = null; final CurrencyData currency = null; final BigDecimal amount = BigDecimal.ZERO; @@ -89,29 +93,30 @@ public class SavingsAccountChargeData { final MonthDay feeOnMonthDay = null; - return new SavingsAccountChargeData(id, chargeId, name, chargeTimeType, dueAsOfDate, chargeCalculationType, percentage, + return new SavingsAccountChargeData(id, chargeId, accountId, name, chargeTimeType, dueAsOfDate, chargeCalculationType, percentage, amountPercentageAppliedTo, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding, chargeOptions, penalty, feeOnMonthDay); } - public static SavingsAccountChargeData instance(final Long id, final Long chargeId, final String name, final CurrencyData currency, + public static SavingsAccountChargeData instance(final Long id, final Long chargeId, final Long accountId, final String name, final CurrencyData currency, final BigDecimal amount, final BigDecimal amountPaid, final BigDecimal amountWaived, final BigDecimal amountWrittenOff, final BigDecimal amountOutstanding, final EnumOptionData chargeTimeType, final LocalDate dueAsOfDate, final EnumOptionData chargeCalculationType, final BigDecimal percentage, final BigDecimal amountPercentageAppliedTo, final Collection chargeOptions, final boolean penalty, final MonthDay feeOnMonthDay) { - return new SavingsAccountChargeData(id, chargeId, name, chargeTimeType, dueAsOfDate, chargeCalculationType, percentage, + return new SavingsAccountChargeData(id, chargeId, accountId, name, chargeTimeType, dueAsOfDate, chargeCalculationType, percentage, amountPercentageAppliedTo, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding, chargeOptions, penalty, feeOnMonthDay); } - private SavingsAccountChargeData(final Long id, final Long chargeId, final String name, final EnumOptionData chargeTimeType, + private SavingsAccountChargeData(final Long id, final Long chargeId, final Long accountId, final String name, final EnumOptionData chargeTimeType, final LocalDate dueAsOfDate, final EnumOptionData chargeCalculationType, final BigDecimal percentage, final BigDecimal amountPercentageAppliedTo, final CurrencyData currency, final BigDecimal amount, final BigDecimal amountPaid, final BigDecimal amountWaived, final BigDecimal amountWrittenOff, final BigDecimal amountOutstanding, final Collection chargeOptions, final boolean penalty, final MonthDay feeOnMonthDay) { this.id = id; this.chargeId = chargeId; + this.accountId = accountId; this.name = name; this.chargeTimeType = chargeTimeType; this.dueDate = dueAsOfDate; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java index 7e6e2d291..b72eb7800 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java @@ -1212,6 +1212,7 @@ public class SavingsAccount extends AbstractPersistable { } } else if (transactionToUndo.isWaiveCharge()) { chargeToUndo.undoWaiver(this.getCurrency(), transactionToUndo.getAmount(this.getCurrency())); + chargeToUndo.updateToPreviousAnnualFeeDueDate(); } } } @@ -1678,6 +1679,9 @@ public class SavingsAccount extends AbstractPersistable { final Money amountWaived = savingsAccountCharge.waive(getCurrency()); handleWaiverChargeTransactions(savingsAccountCharge, amountWaived); + + //update next annual fee due date + savingsAccountCharge.updateToNextAnnualFeeDueDate(); } public void addCharge(final DateTimeFormatter formatter, final SavingsAccountCharge savingsAccountCharge, final Charge chargeDefinition) { @@ -1733,13 +1737,21 @@ public class SavingsAccount extends AbstractPersistable { "multiple.annual.fee.per.account.not.supported"); throw new PlatformApiDataValidationException(dataValidationErrors); } + + if(isActive()){ + savingsAccountCharge.updateToNextAnnualFeeDueDateFrom(getActivationLocalDate()); + }else if(isApproved()){ + savingsAccountCharge.updateToNextAnnualFeeDueDateFrom(getApprovedOnLocalDate()); + } + //if account is in submitted and pending approval state, + //upon account activation annual fee charges due date get updated. } //activation charge and withdrawal charges not required this validation if(savingsAccountCharge.isOnSpecifiedDueDate()){ validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_APPLY_CHARGE, chargeDueDate); } - + // add new charge to savings account charges().add(savingsAccountCharge); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java index 77765cc0e..88021b676 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java @@ -211,7 +211,7 @@ public final class SavingsAccountTransaction extends AbstractPersistable { } public boolean isPostInterestCalculationRequired(){ - return this.isDeposit() || this.isWithdrawal() || this.isPayCharge(); + return this.isDeposit() || this.isChargeTransaction(); } public boolean isInterestPostingAndNotReversed() { @@ -278,7 +278,14 @@ public final class SavingsAccountTransaction extends AbstractPersistable { } public void updateCumulativeBalanceAndDates(final MonetaryCurrency currency, final LocalDate endOfBalanceDate) { - this.balanceEndDate = endOfBalanceDate.toDate(); + //balance end date should not be before transaction date + if (endOfBalanceDate != null && endOfBalanceDate.isBefore(this.transactionLocalDate())){ + this.balanceEndDate = this.transactionLocalDate().toDate(); + }else if(endOfBalanceDate != null){ + this.balanceEndDate = endOfBalanceDate.toDate(); + }else{ + this.balanceEndDate = null; + } this.balanceNumberOfDays = LocalDateInterval.create(getTransactionLocalDate(), endOfBalanceDate).daysInPeriodInclusiveOfEndDate(); this.cumulativeBalance = Money.of(currency, this.runningBalance).multipliedBy(this.balanceNumberOfDays).getAmount(); } @@ -374,7 +381,7 @@ public final class SavingsAccountTransaction extends AbstractPersistable { Money endOfDayBalance = openingBalance.copy(); if (isDeposit()) { endOfDayBalance = openingBalance.plus(getAmount(currency)); - } else if (isWithdrawal() || isWithdrawalFeeAndNotReversed()) { + } else if (isWithdrawal() || isChargeTransactionAndNotReversed()) { endOfDayBalance = openingBalance.minus(getAmount(currency)); } @@ -390,7 +397,7 @@ public final class SavingsAccountTransaction extends AbstractPersistable { Money endOfDayBalance = openingBalance.copy(); if (isDeposit()) { endOfDayBalance = openingBalance.plus(getAmount(currency)); - } else if (isWithdrawal() || isWithdrawalFeeAndNotReversed()) { + } else if (isWithdrawal() || isChargeTransactionAndNotReversed()) { if (openingBalance.isGreaterThanZero()) { endOfDayBalance = openingBalance.minus(getAmount(currency)); @@ -420,7 +427,7 @@ public final class SavingsAccountTransaction extends AbstractPersistable { // if (endOfDayBalance.isLessThanZero()) { endOfDayBalance = Money.of(currency, this.runningBalance); // } - } else if (isWithdrawal() || isWithdrawalFeeAndNotReversed()) { + } else if (isWithdrawal() || isChargeTransactionAndNotReversed()) { // endOfDayBalance = openingBalance.minus(getAmount(currency)); // if (endOfDayBalance.isLessThanZero()) { endOfDayBalance = Money.of(currency, this.runningBalance); @@ -471,6 +478,10 @@ public final class SavingsAccountTransaction extends AbstractPersistable { return SavingsAccountTransactionType.fromInt(this.typeOf).isChargeTransaction(); } + public boolean isChargeTransactionAndNotReversed(){ + return SavingsAccountTransactionType.fromInt(this.typeOf).isChargeTransaction() && isNotReversed(); + } + public boolean isWaiveCharge(){ return SavingsAccountTransactionType.fromInt(this.typeOf).isWaiveCharge(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountChargeReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountChargeReadPlatformService.java index 6708b95e6..9fc9b6ae9 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountChargeReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountChargeReadPlatformService.java @@ -8,6 +8,7 @@ package org.mifosplatform.portfolio.savings.service; import java.util.Collection; import org.mifosplatform.portfolio.charge.data.ChargeData; +import org.mifosplatform.portfolio.savings.data.SavingsAccountAnnualFeeData; import org.mifosplatform.portfolio.savings.data.SavingsAccountChargeData; public interface SavingsAccountChargeReadPlatformService { @@ -17,4 +18,8 @@ public interface SavingsAccountChargeReadPlatformService { Collection retrieveSavingsAccountCharges(Long savingsAccountId); SavingsAccountChargeData retrieveSavingsAccountChargeDetails(Long savingsAccountChargeId, Long savingsAccountId); + + Collection retrieveChargesWithAnnualFeeDue(); + + Collection retrieveChargesWithDue(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountChargeReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountChargeReadPlatformServiceImpl.java index 845cfe3be..2516c605f 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountChargeReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountChargeReadPlatformServiceImpl.java @@ -19,10 +19,13 @@ import org.mifosplatform.infrastructure.core.service.RoutingDataSource; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.portfolio.charge.data.ChargeData; +import org.mifosplatform.portfolio.charge.domain.ChargeTimeType; import org.mifosplatform.portfolio.charge.exception.SavingsAccountChargeNotFoundException; import org.mifosplatform.portfolio.charge.service.ChargeDropdownReadPlatformService; import org.mifosplatform.portfolio.charge.service.ChargeEnumerations; +import org.mifosplatform.portfolio.savings.data.SavingsAccountAnnualFeeData; import org.mifosplatform.portfolio.savings.data.SavingsAccountChargeData; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountStatusType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; @@ -35,6 +38,9 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou private final JdbcTemplate jdbcTemplate; private final PlatformSecurityContext context; private final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService; + + //mappers + private final SavingsAccountChargeDueMapper chargeDueMapper; @Autowired public SavingsAccountChargeReadPlatformServiceImpl(final PlatformSecurityContext context, @@ -42,12 +48,13 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou this.context = context; this.chargeDropdownReadPlatformService = chargeDropdownReadPlatformService; this.jdbcTemplate = new JdbcTemplate(dataSource); + this.chargeDueMapper = new SavingsAccountChargeDueMapper(); } private static final class SavingsAccountChargeMapper implements RowMapper { public String schema() { - return "sc.id as id, c.id as chargeId, c.name as name, " + return "sc.id as id, c.id as chargeId, sc.savings_account_id as accountId, c.name as name, " + "sc.amount as amountDue, " + "sc.amount_paid_derived as amountPaid, " + "sc.amount_waived_derived as amountWaived, " @@ -72,6 +79,7 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou final Long id = rs.getLong("id"); final Long chargeId = rs.getLong("chargeId"); + final Long accountId = rs.getLong("accountId"); final String name = rs.getString("name"); final BigDecimal amount = rs.getBigDecimal("amountDue"); final BigDecimal amountPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountPaid"); @@ -102,16 +110,16 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou if (feeOnDay != null) { feeOnMonthDay = new MonthDay(feeOnMonth, feeOnDay); } - + final int chargeCalculation = rs.getInt("chargeCalculation"); final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(chargeCalculation); final boolean penalty = rs.getBoolean("penalty"); final Collection chargeOptions = null; - return SavingsAccountChargeData.instance(id, chargeId, name, currency, amount, amountPaid, amountWaived, amountWrittenOff, - amountOutstanding, chargeTimeType, dueAsOfDate, chargeCalculationType, percentageOf, amountPercentageAppliedTo, - chargeOptions, penalty, feeOnMonthDay); + return SavingsAccountChargeData.instance(id, chargeId, accountId, name, currency, amount, amountPaid, amountWaived, + amountWrittenOff, amountOutstanding, chargeTimeType, dueAsOfDate, chargeCalculationType, percentageOf, + amountPercentageAppliedTo, chargeOptions, penalty, feeOnMonthDay); } } @@ -161,4 +169,56 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou return this.jdbcTemplate.query(sql, rm, new Object[] { loanId }); } + + private static final class SavingsAccountChargeDueMapper implements RowMapper { + + private final String schemaSql; + + public SavingsAccountChargeDueMapper() { + final StringBuilder sqlBuilder = new StringBuilder(200); + sqlBuilder.append("sac.id as id, "); + sqlBuilder.append("sa.id as accountId, "); + sqlBuilder.append("sa.account_no as accountNo, "); + sqlBuilder.append("sac.charge_due_date as dueDate "); + sqlBuilder.append("from m_savings_account_charge sac join m_savings_account sa on sac.savings_account_id = sa.id "); + + this.schemaSql = sqlBuilder.toString(); + } + + public String schema() { + return this.schemaSql; + } + + @Override + public SavingsAccountAnnualFeeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = rs.getLong("id"); + final Long accountId = rs.getLong("accountId"); + final String accountNo = rs.getString("accountNo"); + final LocalDate annualFeeNextDueDate = JdbcSupport.getLocalDate(rs, "dueDate"); + + return SavingsAccountAnnualFeeData.instance(id, accountId, accountNo, annualFeeNextDueDate); + } + } + + @Override + public Collection retrieveChargesWithAnnualFeeDue() { + final String sql = "select " + this.chargeDueMapper.schema() + " where sac.charge_due_date is not null and sac.charge_time_enum = " + + ChargeTimeType.ANNUAL_FEE.getValue() + " and sac.charge_due_date <= NOW() and sa.status_enum = " + + SavingsAccountStatusType.ACTIVE.getValue(); + + return this.jdbcTemplate.query(sql, this.chargeDueMapper, new Object[] {}); + } + + @Override + public Collection retrieveChargesWithDue() { + final String sql = "select " + + this.chargeDueMapper.schema() + + " where sac.charge_due_date is not null and sac.charge_due_date <= NOW() and sac.waived = 0 and sac.is_paid_derived=0 and sac.is_active=1 and sa.status_enum = " + + SavingsAccountStatusType.ACTIVE.getValue() + " order by sac.charge_due_date "; + + return this.jdbcTemplate.query(sql, this.chargeDueMapper, new Object[] {}); + + } + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java index 03c6ce906..1fa993980 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java @@ -20,7 +20,7 @@ public interface SavingsAccountWritePlatformService { CommandProcessingResult withdrawal(Long savingsId, JsonCommand command); - /*CommandProcessingResult applyAnnualFee(Long savingsId, LocalDate annualFeeTransactionDate);*/ + CommandProcessingResult applyAnnualFee(final Long savingsAccountChargeId, final Long accountId); CommandProcessingResult calculateInterest(Long savingsId); @@ -52,5 +52,5 @@ public interface SavingsAccountWritePlatformService { CommandProcessingResult payCharge(Long savingsAccountId, Long savingsAccountChargeId, JsonCommand command); - void processDueCharges(); + void applyChargeDue(final Long savingsAccountChargeId, final Long accountId); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java index 288612d2d..097798150 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java @@ -248,36 +248,25 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi .build(); } - /*@Transactional + @Transactional @Override - public CommandProcessingResult applyAnnualFee(final Long savingsId, final LocalDate annualFeeTransactionDate) { + public CommandProcessingResult applyAnnualFee(final Long savingsAccountChargeId, final Long accountId) { - final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId); - checkClientOrGroupActive(account); + final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection( + savingsAccountChargeId, accountId); final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd MM yyyy"); - final Set existingTransactionIds = new HashSet(); - final Set existingReversedTransactionIds = new HashSet(); - updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); - - final MathContext mc = MathContext.DECIMAL64; - final LocalDate today = DateUtils.getLocalDateOfTenant(); - - final SavingsAccountTransaction annualFee = account.addAnnualFee(mc, fmt, annualFeeTransactionDate, today); - final Long transactionId = saveTransactionToGenerateTransactionId(annualFee); - this.savingAccountRepository.save(account); - - postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + this.payCharge(savingsAccountCharge, savingsAccountCharge.getDueLocalDate(), savingsAccountCharge.amount(), fmt); return new CommandProcessingResultBuilder() // - .withEntityId(transactionId) // - .withOfficeId(account.officeId()) // - .withClientId(account.clientId()) // - .withGroupId(account.groupId()) // - .withSavingsId(savingsId) // + .withEntityId(savingsAccountCharge.getId()) // + .withOfficeId(savingsAccountCharge.savingsAccount().officeId()) // + .withClientId(savingsAccountCharge.savingsAccount().clientId()) // + .withGroupId(savingsAccountCharge.savingsAccount().groupId()) // + .withSavingsId(savingsAccountCharge.savingsAccount().getId()) // .build(); - }*/ + } @Transactional @Override @@ -828,21 +817,16 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi } + @Transactional @Override - @CronTarget(jobName = JobName.PAY_DUE_SAVINGS_CHARGES) - public void processDueCharges() { + public void applyChargeDue(final Long savingsAccountChargeId, final Long accountId) { // always use current date as transaction date for batch job final LocalDate transactionDate = DateUtils.getLocalDateOfTenant(); - final List pendingCharges = this.savingsAccountChargeRepository.findPendingCharges(transactionDate.toDate()); + final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection( + savingsAccountChargeId, accountId); + final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd MM yyyy"); - for (SavingsAccountCharge savingsAccountCharge : pendingCharges) { - try { - payCharge(savingsAccountCharge, transactionDate, savingsAccountCharge.amoutOutstanding(), fmt); - } catch (Exception exception) { - // TODO: AA - provide a meaningful error with failed - // transactions list - } - } + payCharge(savingsAccountCharge, transactionDate, savingsAccountCharge.amoutOutstanding(), fmt); } @Transactional @@ -857,7 +841,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi account.payCharge(savingsAccountCharge, amountPaid, transactionDate, formatter); final MathContext mc = MathContext.DECIMAL64; - if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate())) { + if (account.isBeforeLastPostingPeriod(transactionDate)) { final LocalDate today = DateUtils.getLocalDateOfTenant(); account.postInterest(mc, today); } else { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/scheduledjobs/service/ScheduledJobRunnerService.java b/mifosng-provider/src/main/java/org/mifosplatform/scheduledjobs/service/ScheduledJobRunnerService.java index 704d43d4b..d1ba2018e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/scheduledjobs/service/ScheduledJobRunnerService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/scheduledjobs/service/ScheduledJobRunnerService.java @@ -14,4 +14,6 @@ public interface ScheduledJobRunnerService { void updateLoanPaidInAdvance(); void applyAnnualFeeForSavings(); + + void applyDueChargesForSavings(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java index 4359425b2..9711b66b5 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java @@ -5,11 +5,17 @@ */ package org.mifosplatform.scheduledjobs.service; +import java.util.Collection; +import java.util.List; + +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.service.RoutingDataSourceServiceFactory; import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil; import org.mifosplatform.infrastructure.jobs.annotation.CronTarget; import org.mifosplatform.infrastructure.jobs.service.JobName; -import org.mifosplatform.portfolio.savings.service.SavingsAccountReadPlatformService; +import org.mifosplatform.portfolio.savings.data.SavingsAccountAnnualFeeData; +import org.mifosplatform.portfolio.savings.service.SavingsAccountChargeReadPlatformService; import org.mifosplatform.portfolio.savings.service.SavingsAccountWritePlatformService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,18 +30,16 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService private final static Logger logger = LoggerFactory.getLogger(ScheduledJobRunnerServiceImpl.class); private final RoutingDataSourceServiceFactory dataSourceServiceFactory; - @SuppressWarnings("unused") private final SavingsAccountWritePlatformService savingsAccountWritePlatformService; - @SuppressWarnings("unused") - private final SavingsAccountReadPlatformService savingsAccountReadPlatformService; + private final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService; @Autowired public ScheduledJobRunnerServiceImpl(final RoutingDataSourceServiceFactory dataSourceServiceFactory, final SavingsAccountWritePlatformService savingsAccountWritePlatformService, - final SavingsAccountReadPlatformService savingsAccountReadPlatformService) { + final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService) { this.dataSourceServiceFactory = dataSourceServiceFactory; this.savingsAccountWritePlatformService = savingsAccountWritePlatformService; - this.savingsAccountReadPlatformService = savingsAccountReadPlatformService; + this.savingsAccountChargeReadPlatformService = savingsAccountChargeReadPlatformService; } @Transactional @@ -194,27 +198,50 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Results affected by update: " + result); } - @Transactional @Override @CronTarget(jobName = JobName.APPLY_ANNUAL_FEE_FOR_SAVINGS) public void applyAnnualFeeForSavings() { - /*final Collection annualFeeData = this.savingsAccountReadPlatformService - .retrieveAccountsWithAnnualFeeDue(); + final Collection annualFeeData = this.savingsAccountChargeReadPlatformService + .retrieveChargesWithAnnualFeeDue(); for (final SavingsAccountAnnualFeeData savingsAccountReference : annualFeeData) { try { this.savingsAccountWritePlatformService.applyAnnualFee(savingsAccountReference.getId(), - savingsAccountReference.getNextAnnualFeeDueDate()); + savingsAccountReference.getAccountId()); } catch (final PlatformApiDataValidationException e) { final List errors = e.getErrors(); for (final ApiParameterError error : errors) { logger.error("Apply annual fee failed for account:" + savingsAccountReference.getAccountNo() + " with message " + error.getDeveloperMessage()); } + } catch (final Exception ex){ + //need to handle this scenario } } - logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Savings accounts affected by update: " + annualFeeData.size());*/ + logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Savings accounts affected by update: " + annualFeeData.size()); } + + @Override + @CronTarget(jobName = JobName.PAY_DUE_SAVINGS_CHARGES) + public void applyDueChargesForSavings() { + final Collection chargesDueData = this.savingsAccountChargeReadPlatformService + .retrieveChargesWithDue(); + + for (final SavingsAccountAnnualFeeData savingsAccountReference : chargesDueData) { + try { + this.savingsAccountWritePlatformService.applyChargeDue(savingsAccountReference.getId(), savingsAccountReference.getAccountId()); + } catch (final PlatformApiDataValidationException e) { + final List errors = e.getErrors(); + for (final ApiParameterError error : errors) { + logger.error("Apply Charges due for savings failed for account:" + savingsAccountReference.getAccountNo() + " with message " + + error.getDeveloperMessage()); + } + } + } + + logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Savings accounts affected by update: " + chargesDueData.size()); + } + } \ No newline at end of file