From 75635b3b6f80dfcdf435853a46b4021d59a79165 Mon Sep 17 00:00:00 2001 From: ashok-conflux Date: Mon, 30 Dec 2013 10:53:04 +0530 Subject: [PATCH] Advanced deposit products and accounts (FD & RD) with interest rate slabs --- .../V164__fd_and_rd_deposit_tables.sql | 237 +++ .../commands/domain/CommandWrapper.java | 102 +- .../service/CommandWrapperBuilder.java | 408 ++++- ...CommandSourceWritePlatformServiceImpl.java | 3 +- .../SynchronousCommandProcessingService.java | 80 + .../infrastructure/core/api/JsonCommand.java | 38 +- .../core/data/DataValidatorBuilder.java | 44 +- .../core/data/EnumOptionData.java | 7 +- .../core/data/PaginationParameters.java | 12 + .../core/domain/JdbcSupport.java | 13 + .../infrastructure/jobs/service/JobName.java | 3 +- .../data/SavingsAccountSummaryData.java | 7 +- ...sReadPlatformServiceJpaRepositoryImpl.java | 9 +- .../client/api/ClientApiConstants.java | 13 +- .../portfolio/client/data/ClientData.java | 55 +- .../client/data/ClientDataValidator.java | 26 +- .../portfolio/client/domain/Client.java | 41 +- .../ClientReadPlatformServiceImpl.java | 36 +- ...WritePlatformServiceJpaRepositoryImpl.java | 11 +- .../InterestRateChartApiConstants.java | 59 + .../InterestRateChartPeriodType.java | 74 + .../InterestRateChartSlabApiConstants.java | 48 + .../InterestRateChartSlabsApiResource.java | 139 ++ .../api/InterestRateChartsApiResource.java | 143 ++ .../data/InterestRateChartData.java | 116 ++ .../data/InterestRateChartDataValidator.java | 176 +++ .../data/InterestRateChartRepository.java | 14 + .../InterestRateChartRepositoryWrapper.java | 53 + .../data/InterestRateChartSlabData.java | 101 ++ .../InterestRateChartSlabDataValidator.java | 207 +++ .../data/InterestRateChartSlabRepository.java | 14 + ...nterestRateChartSlabRepositoryWrapper.java | 53 + .../domain/InterestRateChart.java | 261 ++++ .../domain/InterestRateChartFields.java | 150 ++ .../domain/InterestRateChartSlab.java | 79 + .../domain/InterestRateChartSlabFields.java | 213 +++ .../InterestRateChartNotFoundException.java | 15 + ...nterestRateChartSlabNotFoundException.java | 19 + ...CreateInterestRateChartCommandHandler.java | 29 + ...teInterestRateChartSlabCommandHandler.java | 29 + ...DeleteInterestRateChartCommandHandler.java | 29 + ...teInterestRateChartSlabCommandHandler.java | 29 + ...UpdateInterestRateChartCommandHandler.java | 29 + ...teInterestRateChartSlabCommandHandler.java | 29 + .../service/InterestRateChartAssembler.java | 94 ++ ...tRateChartDropdownReadPlatformService.java | 15 + ...eChartDropdownReadPlatformServiceImpl.java | 22 + .../InterestRateChartEnumerations.java | 57 + .../InterestRateChartReadPlatformService.java | 33 + ...erestRateChartReadPlatformServiceImpl.java | 279 ++++ .../InterestRateChartSlabAssembler.java | 128 ++ ...erestRateChartSlabReadPlatformService.java | 19 + ...tRateChartSlabReadPlatformServiceImpl.java | 119 ++ ...restRateChartSlabWritePlatformService.java | 18 + ...WritePlatformServiceJpaRepositoryImpl.java | 105 ++ ...InterestRateChartWritePlatformService.java | 18 + ...WritePlatformServiceJpaRepositoryImpl.java | 92 ++ ...WritePlatformServiceJpaRepositoryImpl.java | 2 +- .../savings/DepositAccountOnClosureType.java | 86 ++ .../portfolio/savings/DepositAccountType.java | 108 ++ .../savings/DepositsApiConstants.java | 357 +++++ .../PreClosurePenalInterestOnType.java | 74 + .../savings/RecurringDepositType.java | 66 + .../savings/SavingsApiConstants.java | 1 + .../savings/SavingsPeriodFrequencyType.java | 18 + ...DepositAccountTransactionsApiResource.java | 173 +++ .../api/FixedDepositAccountsApiResource.java | 318 ++++ .../api/FixedDepositProductsApiResource.java | 316 ++++ ...DepositAccountTransactionsApiResource.java | 185 +++ .../RecurringDepositAccountsApiResource.java | 324 ++++ .../RecurringDepositProductsApiResource.java | 318 ++++ ...SavingsAccountTransactionsApiResource.java | 5 +- .../api/SavingsAccountsApiResource.java | 3 +- .../savings/data/DepositAccountData.java | 280 ++++ .../data/DepositAccountDataValidator.java | 613 ++++++++ .../DepositAccountInterestRateChartData.java | 120 ++ ...positAccountInterestRateChartSlabData.java | 68 + ...epositAccountTransactionDataValidator.java | 190 +++ .../savings/data/DepositProductData.java | 364 +++++ .../data/DepositProductDataValidator.java | 736 +++++++++ .../savings/data/FixedDepositAccountData.java | 486 ++++++ .../savings/data/FixedDepositProductData.java | 392 +++++ .../data/RecurringDepositAccountData.java | 536 +++++++ .../data/RecurringDepositProductData.java | 433 ++++++ .../data/SavingsAccountStatusEnumData.java | 5 +- .../domain/DepositAccountAssembler.java | 368 +++++ .../domain/DepositAccountDomainService.java | 30 + .../DepositAccountDomainServiceJpa.java | 320 ++++ .../DepositAccountInterestRateChart.java | 130 ++ .../DepositAccountInterestRateChartSlabs.java | 59 + .../domain/DepositAccountRecurringDetail.java | 101 ++ .../DepositAccountTermAndPreClosure.java | 238 +++ .../domain/DepositPreClosureDetail.java | 231 +++ .../domain/DepositProductAssembler.java | 538 +++++++ .../domain/DepositProductRecurringDetail.java | 61 + .../DepositProductTermAndPreClosure.java | 75 + .../domain/DepositRecurringDetail.java | 105 ++ .../savings/domain/DepositTermDetail.java | 246 +++ .../savings/domain/FixedDepositAccount.java | 697 +++++++++ .../domain/FixedDepositAccountRepository.java | 14 + .../savings/domain/FixedDepositProduct.java | 264 ++++ .../domain/FixedDepositProductRepository.java | 13 + .../domain/RecurringDepositAccount.java | 820 ++++++++++ .../RecurringDepositAccountRepository.java | 13 + .../domain/RecurringDepositProduct.java | 120 ++ .../RecurringDepositProductRepository.java | 13 + .../savings/domain/SavingsAccount.java | 175 ++- .../domain/SavingsAccountAssembler.java | 5 +- .../domain/SavingsAccountRepository.java | 3 + .../SavingsAccountRepositoryWrapper.java | 7 + .../domain/SavingsAccountStatusType.java | 19 +- .../savings/domain/SavingsAccountSummary.java | 4 +- .../savings/domain/SavingsHelper.java | 11 +- .../savings/domain/SavingsProduct.java | 176 +-- .../interest/DailyCompoundingPeriod.java | 16 +- .../domain/interest/EndOfDayBalance.java | 15 +- .../interest/MonthlyCompoundingPeriod.java | 11 +- .../domain/interest/PostingPeriod.java | 37 +- ...untInterestRateChartNotFoundException.java | 15 + .../DepositAccountNotFoundException.java | 18 + ...AccountTransactionNotAllowedException.java | 17 + .../FixedDepositProductNotFoundException.java | 15 + ...urringDepositProductNotFoundException.java | 15 + .../ActivateDepositAccountCommandHandler.java | 31 + ...eInterestDepositAccountCommandHandler.java | 31 + .../CloseDepositAccountCommandHandler.java | 31 + ...eateFixedDepositProductCommandHandler.java | 29 + ...RecurringDepositProductCommandHandler.java | 29 + ...leteFixedDepositProductCommandHandler.java | 29 + ...RecurringDepositProductCommandHandler.java | 29 + ...ountApplicationApprovalCommandHandler.java | 32 + ...ApplicationApprovalUndoCommandHandler.java | 32 + ...ountApplicationDeletionCommandHandler.java | 32 + ...ApplicationModificationCommandHandler.java | 32 + ...ountApplicationRejectedCommandHandler.java | 32 + ...untApplicationSubmittalCommandHandler.java | 32 + ...ionWithdrawnByApplicantCommandHandler.java | 33 + .../DepositAccountDepositCommandHandler.java | 31 + ...itTransactionAdjustmentCommandHandler.java | 33 + ...tInterestDepositAccountCommandHandler.java | 31 + ...tureCloseDepositAccountCommandHandler.java | 31 + ...ansactionDepositAccountCommandHandler.java | 33 + ...dateFixedDepositProductCommandHandler.java | 29 + ...RecurringDepositProductCommandHandler.java | 29 + .../WithdrawDepositAccountCommandHandler.java | 31 + ...tInterestRateChartReadPlatformService.java | 21 + ...erestRateChartReadPlatformServiceImpl.java | 251 +++ ...ntPreMatureCalculationPlatformService.java | 15 + ...eMatureCalculationPlatformServiceImpl.java | 87 ++ .../DepositAccountReadPlatformService.java | 37 + ...DepositAccountReadPlatformServiceImpl.java | 1372 +++++++++++++++++ .../DepositAccountWritePlatformService.java | 59 + ...WritePlatformServiceJpaRepositoryImpl.java | 912 +++++++++++ ...pplicationProcessWritePlatformService.java | 27 + ...WritePlatformServiceJpaRepositoryImpl.java | 464 ++++++ .../DepositProductReadPlatformService.java | 23 + ...DepositProductReadPlatformServiceImpl.java | 363 +++++ .../DepositsDropdownReadPlatformService.java | 31 + ...positsDropdownReadPlatformServiceImpl.java | 56 + ...xedDepositProductWritePlatformService.java | 18 + ...WritePlatformServiceJpaRepositoryImpl.java | 168 ++ ...ingDepositProductWritePlatformService.java | 18 + ...WritePlatformServiceJpaRepositoryImpl.java | 168 ++ .../SavingsAccountReadPlatformService.java | 9 +- ...SavingsAccountReadPlatformServiceImpl.java | 29 +- ...WritePlatformServiceJpaRepositoryImpl.java | 2 +- .../savings/service/SavingsEnumerations.java | 339 +++- ...SavingsProductReadPlatformServiceImpl.java | 42 +- .../service/ScheduledJobRunnerService.java | 4 +- .../ScheduledJobRunnerServiceImpl.java | 36 +- 170 files changed, 20697 insertions(+), 380 deletions(-) create mode 100644 mifosng-db/migrations/core_db/V164__fd_and_rd_deposit_tables.sql create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/InterestRateChartApiConstants.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/InterestRateChartPeriodType.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/InterestRateChartSlabApiConstants.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/api/InterestRateChartSlabsApiResource.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/api/InterestRateChartsApiResource.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartData.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartDataValidator.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartRepository.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartRepositoryWrapper.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartSlabData.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartSlabDataValidator.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartSlabRepository.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartSlabRepositoryWrapper.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/domain/InterestRateChart.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/domain/InterestRateChartFields.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/domain/InterestRateChartSlab.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/domain/InterestRateChartSlabFields.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/exception/InterestRateChartNotFoundException.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/exception/InterestRateChartSlabNotFoundException.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/CreateInterestRateChartCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/CreateInterestRateChartSlabCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/DeleteInterestRateChartCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/DeleteInterestRateChartSlabCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/UpdateInterestRateChartCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/UpdateInterestRateChartSlabCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartAssembler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartDropdownReadPlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartDropdownReadPlatformServiceImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartEnumerations.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartReadPlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartReadPlatformServiceImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartSlabAssembler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartSlabReadPlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartSlabReadPlatformServiceImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartSlabWritePlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartSlabWritePlatformServiceJpaRepositoryImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartWritePlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartWritePlatformServiceJpaRepositoryImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/DepositAccountOnClosureType.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/DepositAccountType.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/DepositsApiConstants.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/PreClosurePenalInterestOnType.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/RecurringDepositType.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/FixedDepositAccountTransactionsApiResource.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/FixedDepositAccountsApiResource.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/FixedDepositProductsApiResource.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/RecurringDepositAccountTransactionsApiResource.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/RecurringDepositAccountsApiResource.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/RecurringDepositProductsApiResource.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountData.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountDataValidator.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountInterestRateChartData.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountInterestRateChartSlabData.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountTransactionDataValidator.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositProductData.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositProductDataValidator.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/FixedDepositAccountData.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/FixedDepositProductData.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/RecurringDepositAccountData.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/RecurringDepositProductData.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountAssembler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountDomainService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountDomainServiceJpa.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountInterestRateChart.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountInterestRateChartSlabs.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountRecurringDetail.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountTermAndPreClosure.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositPreClosureDetail.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositProductAssembler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositProductRecurringDetail.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositProductTermAndPreClosure.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositRecurringDetail.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositTermDetail.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/FixedDepositAccount.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/FixedDepositAccountRepository.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/FixedDepositProduct.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/FixedDepositProductRepository.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/RecurringDepositAccount.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/RecurringDepositAccountRepository.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/RecurringDepositProduct.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/RecurringDepositProductRepository.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/DepositAccountInterestRateChartNotFoundException.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/DepositAccountNotFoundException.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/DepositAccountTransactionNotAllowedException.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/FixedDepositProductNotFoundException.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/RecurringDepositProductNotFoundException.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/ActivateDepositAccountCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/CalculateInterestDepositAccountCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/CloseDepositAccountCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/CreateFixedDepositProductCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/CreateRecurringDepositProductCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DeleteFixedDepositProductCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DeleteRecurringDepositProductCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationApprovalCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationApprovalUndoCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationDeletionCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationModificationCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationRejectedCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationSubmittalCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationWithdrawnByApplicantCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountDepositCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositTransactionAdjustmentCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/PostInterestDepositAccountCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/PrematureCloseDepositAccountCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/UndoTransactionDepositAccountCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/UpdateFixedDepositProductCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/UpdateRecurringDepositProductCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/WithdrawDepositAccountCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountInterestRateChartReadPlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountInterestRateChartReadPlatformServiceImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountPreMatureCalculationPlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountPreMatureCalculationPlatformServiceImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositApplicationProcessWritePlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositProductReadPlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositProductReadPlatformServiceImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositsDropdownReadPlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositsDropdownReadPlatformServiceImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/FixedDepositProductWritePlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/FixedDepositProductWritePlatformServiceJpaRepositoryImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/RecurringDepositProductWritePlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/RecurringDepositProductWritePlatformServiceJpaRepositoryImpl.java diff --git a/mifosng-db/migrations/core_db/V164__fd_and_rd_deposit_tables.sql b/mifosng-db/migrations/core_db/V164__fd_and_rd_deposit_tables.sql new file mode 100644 index 000000000..e4ba3278e --- /dev/null +++ b/mifosng-db/migrations/core_db/V164__fd_and_rd_deposit_tables.sql @@ -0,0 +1,237 @@ +CREATE TABLE IF NOT EXISTS `m_interest_rate_chart` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `name` varchar(100) DEFAULT NULL, + `description` varchar(200) DEFAULT NULL, + `from_date` date NOT NULL, + `end_date` date DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +CREATE TABLE IF NOT EXISTS `m_interest_rate_slab` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `interest_rate_chart_id` bigint(20) NOT NULL, + `description` varchar(200) DEFAULT NULL, + `period_type_enum` smallint(5) NOT NULL DEFAULT '1', + `from_period` int(11) NOT NULL DEFAULT '0', + `to_period` int(11) DEFAULT NULL, + `amount_range_from` decimal(19,6) DEFAULT NULL, + `amount_range_to` decimal(19,6) DEFAULT NULL, + `annual_interest_rate` decimal(19,6) NOT NULL, + `currency_code` varchar(3) NOT NULL, + PRIMARY KEY (`id`), + KEY `FKIRS00000000000001` (`interest_rate_chart_id`), + CONSTRAINT `FKIRS00000000000001` FOREIGN KEY (`interest_rate_chart_id`) REFERENCES `m_interest_rate_chart` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `m_deposit_product_interest_rate_chart` ( + `deposit_product_id` bigint(20) NOT NULL, + `interest_rate_chart_id` bigint(20) NOT NULL, + UNIQUE KEY `deposit_product_id_interest_rate_chart_id` (`deposit_product_id`,`interest_rate_chart_id`), + KEY `FKDPIRC00000000000002` (`interest_rate_chart_id`), + CONSTRAINT `FKDPIRC00000000000001` FOREIGN KEY (`deposit_product_id`) REFERENCES `m_savings_product` (`id`), + CONSTRAINT `FKDPIRC00000000000002` FOREIGN KEY (`interest_rate_chart_id`) REFERENCES `m_interest_rate_chart` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +CREATE TABLE IF NOT EXISTS `m_deposit_product_term_and_preclosure` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `savings_product_id` bigint(20) NOT NULL DEFAULT '0', + `min_deposit_term` int(11) DEFAULT NULL, + `max_deposit_term` int(11) DEFAULT NULL, + `min_deposit_term_type_enum` smallint(5) DEFAULT NULL, + `max_deposit_term_type_enum` smallint(5) DEFAULT NULL, + `in_multiples_of_deposit_term` int(11) DEFAULT NULL, + `in_multiples_of_deposit_term_type_enum` smallint(5) DEFAULT NULL, + `interest_free_period_applicable` smallint(5) DEFAULT NULL, + `interest_free_from_period` int(11) DEFAULT NULL, + `interest_free_to_period` int(11) DEFAULT NULL, + `interest_free_period_frequency_enum` smallint(5) DEFAULT NULL, + `pre_closure_penal_applicable` smallint(5) DEFAULT NULL, + `pre_closure_penal_interest` decimal(19,6) DEFAULT NULL, + `pre_closure_penal_interest_on_enum` smallint(5) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `FKDPTP00000000000001` (`savings_product_id`), + CONSTRAINT `FKDPTP00000000000001` FOREIGN KEY (`savings_product_id`) REFERENCES `m_savings_product` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +CREATE TABLE IF NOT EXISTS `m_deposit_product_recurring_detail` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `savings_product_id` bigint(20) NOT NULL DEFAULT '0', + `recurring_deposit_type_enum` smallint(5) DEFAULT NULL, + `recurring_deposit_frequency` int(11) DEFAULT NULL, + `recurring_deposit_frequency_type_enum` smallint(5) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `FKDPRD00000000000001` (`savings_product_id`), + CONSTRAINT `FKDPRD00000000000001` FOREIGN KEY (`savings_product_id`) REFERENCES `m_savings_product` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `m_savings_account_interest_rate_chart` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `savings_account_id` bigint(20) NOT NULL, + `name` varchar(100) DEFAULT NULL, + `description` varchar(200) DEFAULT NULL, + `from_date` date NOT NULL, + `end_date` date DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `FKSAIRC00000000000001` (`savings_account_id`), + CONSTRAINT `FKSAIRC00000000000001` FOREIGN KEY (`savings_account_id`) REFERENCES `m_savings_account` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `m_savings_account_interest_rate_slab` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `savings_account_interest_rate_chart_id` bigint(20) NOT NULL, + `description` varchar(200) DEFAULT NULL, + `period_type_enum` smallint(5) NOT NULL DEFAULT '1', + `from_period` int(11) NOT NULL DEFAULT '0', + `to_period` int(11) DEFAULT NULL, + `amount_range_from` decimal(19,6) DEFAULT NULL, + `amount_range_to` decimal(19,6) DEFAULT NULL, + `annual_interest_rate` decimal(19,6) NOT NULL, + `currency_code` varchar(3) NOT NULL, + PRIMARY KEY (`id`), + KEY `FKSAIRS00000000000001` (`savings_account_interest_rate_chart_id`), + CONSTRAINT `FKSAIRS00000000000001` FOREIGN KEY (`savings_account_interest_rate_chart_id`) REFERENCES `m_savings_account_interest_rate_chart` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `m_deposit_account_recurring_detail` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `savings_account_id` bigint(20) NOT NULL DEFAULT '0', + `recurring_deposit_amount` decimal(19,6) DEFAULT NULL, + `recurring_deposit_type_enum` smallint(5) DEFAULT NULL, + `recurring_deposit_frequency` int(11) DEFAULT NULL, + `recurring_deposit_frequency_type_enum` smallint(5) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `FKDARD00000000000001` (`savings_account_id`), + CONSTRAINT `FKDARD00000000000001` FOREIGN KEY (`savings_account_id`) REFERENCES `m_savings_account` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `m_deposit_account_term_and_preclosure` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `savings_account_id` bigint(20) NOT NULL DEFAULT '0', + `min_deposit_term` int(11) DEFAULT NULL, + `max_deposit_term` int(11) DEFAULT NULL, + `min_deposit_term_type_enum` smallint(5) DEFAULT NULL, + `max_deposit_term_type_enum` smallint(5) DEFAULT NULL, + `in_multiples_of_deposit_term` int(11) DEFAULT NULL, + `in_multiples_of_deposit_term_type_enum` smallint(5) DEFAULT NULL, + `interest_free_period_applicable` smallint(5) DEFAULT NULL, + `interest_free_from_period` int(11) DEFAULT NULL, + `interest_free_to_period` int(11) DEFAULT NULL, + `interest_free_period_frequency_enum` smallint(5) DEFAULT NULL, + `pre_closure_penal_applicable` smallint(5) DEFAULT NULL, + `pre_closure_penal_interest` decimal(19,6) DEFAULT NULL, + `pre_closure_penal_interest_on_enum` smallint(5) DEFAULT NULL, + `deposit_period` int(11) DEFAULT NULL, + `deposit_period_frequency_enum` smallint(5) DEFAULT NULL, + `deposit_amount` decimal(19,6) DEFAULT NULL, + `maturity_amount` decimal(19,6) DEFAULT NULL, + `maturity_date` date DEFAULT NULL, + `on_account_closure_enum` smallint(5) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `FKDATP00000000000001` (`savings_account_id`), + CONSTRAINT `FKDATP00000000000001` FOREIGN KEY (`savings_account_id`) REFERENCES `m_savings_account` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +ALTER TABLE `m_savings_product` + ADD COLUMN `deposit_type_enum` SMALLINT(5) NOT NULL DEFAULT '100' AFTER `description`; + +ALTER TABLE `m_savings_account` + ADD COLUMN `deposit_type_enum` SMALLINT(5) NOT NULL DEFAULT '100' AFTER `account_type_enum`; + +ALTER TABLE `m_client` + ADD COLUMN `gender_cv_id` INT(11) NULL DEFAULT NULL AFTER `mobile_no`, + ADD COLUMN `date_of_birth` DATE NULL DEFAULT NULL AFTER `gender_cv_id`; + + +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'CREATE_INTERESTRATECHART', 'INTERESTRATECHART', 'CREATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'CREATE_INTERESTRATECHART_CHECKER', 'INTERESTRATECHART', 'CREATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'UPDATE_INTERESTRATECHART', 'INTERESTRATECHART', 'UPDATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'DELETE_INTERESTRATECHART', 'INTERESTRATECHART', 'DELETE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'UPDATE_INTERESTRATECHART_CHECKER', 'INTERESTRATECHART', 'UPDATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'DELETE_INTERESTRATECHART_CHECKER', 'INTERESTRATECHART', 'DELETE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'CREATE_CHARTSLAB', 'CHARTSLAB', 'CREATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'CREATE_CHARTSLAB_CHECKER', 'CHARTSLAB', 'CREATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'UPDATE_CHARTSLAB', 'CHARTSLAB', 'UPDATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'DELETE_CHARTSLAB', 'CHARTSLAB', 'DELETE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'UPDATE_CHARTSLAB_CHECKER', 'CHARTSLAB', 'UPDATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'DELETE_CHARTSLAB_CHECKER', 'CHARTSLAB', 'DELETE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'CREATE_FIXEDDEPOSITPRODUCT', 'FIXEDDEPOSITPRODUCT', 'CREATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'CREATE_FIXEDDEPOSITPRODUCT_CHECKER', 'FIXEDDEPOSITPRODUCT', 'CREATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'UPDATE_FIXEDDEPOSITPRODUCT', 'FIXEDDEPOSITPRODUCT', 'UPDATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'DELETE_FIXEDDEPOSITPRODUCT', 'FIXEDDEPOSITPRODUCT', 'DELETE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'UPDATE_FIXEDDEPOSITPRODUCT_CHECKER', 'FIXEDDEPOSITPRODUCT', 'UPDATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'DELETE_FIXEDDEPOSITPRODUCT_CHECKER', 'FIXEDDEPOSITPRODUCT', 'DELETE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'CREATE_RECURRINGDEPOSITPRODUCT', 'RECURRINGDEPOSITPRODUCT', 'CREATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'CREATE_RECURRINGDEPOSITPRODUCT_CHECKER', 'RECURRINGDEPOSITPRODUCT', 'CREATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'UPDATE_RECURRINGDEPOSITPRODUCT', 'RECURRINGDEPOSITPRODUCT', 'UPDATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'DELETE_RECURRINGDEPOSITPRODUCT', 'RECURRINGDEPOSITPRODUCT', 'DELETE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'UPDATE_RECURRINGDEPOSITPRODUCT_CHECKER', 'RECURRINGDEPOSITPRODUCT', 'UPDATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'DELETE_RECURRINGDEPOSITPRODUCT_CHECKER', 'RECURRINGDEPOSITPRODUCT', 'DELETE', 0); + +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'READ_FIXEDDEPOSITACCOUNT', 'FIXEDDEPOSITACCOUNT', 'READ', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'CREATE_FIXEDDEPOSITACCOUNT', 'FIXEDDEPOSITACCOUNT', 'CREATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'CREATE_FIXEDDEPOSITACCOUNT_CHECKER', 'FIXEDDEPOSITACCOUNT', 'CREATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'UPDATE_FIXEDDEPOSITACCOUNT', 'FIXEDDEPOSITACCOUNT', 'UPDATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'UPDATE_FIXEDDEPOSITACCOUNT_CHECKER', 'FIXEDDEPOSITACCOUNT', 'UPDATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'DELETE_FIXEDDEPOSITACCOUNT', 'FIXEDDEPOSITACCOUNT', 'DELETE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'DELETE_FIXEDDEPOSITACCOUNT_CHECKER', 'FIXEDDEPOSITACCOUNT', 'DELETE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'DEPOSIT_FIXEDDEPOSITACCOUNT', 'FIXEDDEPOSITACCOUNT', 'DEPOSIT', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'DEPOSIT_FIXEDDEPOSITACCOUNT_CHECKER', 'FIXEDDEPOSITACCOUNT', 'DEPOSIT', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'WITHDRAWAL_FIXEDDEPOSITACCOUNT', 'FIXEDDEPOSITACCOUNT', 'WITHDRAWAL', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'WITHDRAWAL_FIXEDDEPOSITACCOUNT_CHECKER', 'FIXEDDEPOSITACCOUNT', 'WITHDRAWAL', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'ACTIVATE_FIXEDDEPOSITACCOUNT', 'FIXEDDEPOSITACCOUNT', 'ACTIVATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'ACTIVATE_FIXEDDEPOSITACCOUNT_CHECKER', 'FIXEDDEPOSITACCOUNT', 'ACTIVATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'CALCULATEINTEREST_FIXEDDEPOSITACCOUNT', 'FIXEDDEPOSITACCOUNT', 'CALCULATEINTEREST', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'CALCULATEINTEREST_FIXEDDEPOSITACCOUNT_CHECKER', 'FIXEDDEPOSITACCOUNT', 'CALCULATEINTEREST', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'POSTINTEREST_FIXEDDEPOSITACCOUNT', 'FIXEDDEPOSITACCOUNT', 'POSTINTEREST', 1); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'POSTINTEREST_FIXEDDEPOSITACCOUNT_CHECKER', 'FIXEDDEPOSITACCOUNT', 'POSTINTEREST', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'APPROVE_FIXEDDEPOSITACCOUNT', 'FIXEDDEPOSITACCOUNT', 'APPROVE', 1); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'REJECT_FIXEDDEPOSITACCOUNT', 'FIXEDDEPOSITACCOUNT', 'REJECT', 1); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'WITHDRAW_FIXEDDEPOSITACCOUNT', 'FIXEDDEPOSITACCOUNT', 'WITHDRAW', 1); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'APPROVALUNDO_FIXEDDEPOSITACCOUNT', 'FIXEDDEPOSITACCOUNT', 'APPROVALUNDO', 1); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'CLOSE_FIXEDDEPOSITACCOUNT', 'FIXEDDEPOSITACCOUNT', 'CLOSE', 1); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'APPROVE_FIXEDDEPOSITACCOUNT_CHECKER', 'FIXEDDEPOSITACCOUNT', 'APPROVE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'REJECT_FIXEDDEPOSITACCOUNT_CHECKER', 'FIXEDDEPOSITACCOUNT', 'REJECT', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'WITHDRAW_FIXEDDEPOSITACCOUNT_CHECKER', 'FIXEDDEPOSITACCOUNT', 'WITHDRAW', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'APPROVALUNDO_FIXEDDEPOSITACCOUNT_CHECKER', 'FIXEDDEPOSITACCOUNT', 'APPROVALUNDO', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'CLOSE_FIXEDDEPOSITACCOUNT_CHECKER', 'FIXEDDEPOSITACCOUNT', 'CLOSE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'UNDOTRANSACTION_FIXEDDEPOSITACCOUNT', 'FIXEDDEPOSITACCOUNT', 'UNDOTRANSACTION', 1); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'UNDOTRANSACTION_FIXEDDEPOSITACCOUNT_CHECKER', 'FIXEDDEPOSITACCOUNT', 'UNDOTRANSACTION', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'ADJUSTTRANSACTION_FIXEDDEPOSITACCOUNT', 'FIXEDDEPOSITACCOUNT', 'ADJUSTTRANSACTION', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'READ_RECURRINGDEPOSITACCOUNT', 'RECURRINGDEPOSITACCOUNT', 'READ', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'CREATE_RECURRINGDEPOSITACCOUNT', 'RECURRINGDEPOSITACCOUNT', 'CREATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'CREATE_RECURRINGDEPOSITACCOUNT_CHECKER', 'RECURRINGDEPOSITACCOUNT', 'CREATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'UPDATE_RECURRINGDEPOSITACCOUNT', 'RECURRINGDEPOSITACCOUNT', 'UPDATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'UPDATE_RECURRINGDEPOSITACCOUNT_CHECKER', 'RECURRINGDEPOSITACCOUNT', 'UPDATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'DELETE_RECURRINGDEPOSITACCOUNT', 'RECURRINGDEPOSITACCOUNT', 'DELETE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'DELETE_RECURRINGDEPOSITACCOUNT_CHECKER', 'RECURRINGDEPOSITACCOUNT', 'DELETE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'DEPOSIT_RECURRINGDEPOSITACCOUNT', 'RECURRINGDEPOSITACCOUNT', 'DEPOSIT', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'DEPOSIT_RECURRINGDEPOSITACCOUNT_CHECKER', 'RECURRINGDEPOSITACCOUNT', 'DEPOSIT', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'WITHDRAWAL_RECURRINGDEPOSITACCOUNT', 'RECURRINGDEPOSITACCOUNT', 'WITHDRAWAL', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'WITHDRAWAL_RECURRINGDEPOSITACCOUNT_CHECKER', 'RECURRINGDEPOSITACCOUNT', 'WITHDRAWAL', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'ACTIVATE_RECURRINGDEPOSITACCOUNT', 'RECURRINGDEPOSITACCOUNT', 'ACTIVATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'ACTIVATE_RECURRINGDEPOSITACCOUNT_CHECKER', 'RECURRINGDEPOSITACCOUNT', 'ACTIVATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'CALCULATEINTEREST_RECURRINGDEPOSITACCOUNT', 'RECURRINGDEPOSITACCOUNT', 'CALCULATEINTEREST', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'CALCULATEINTEREST_RECURRINGDEPOSITACCOUNT_CHECKER', 'RECURRINGDEPOSITACCOUNT', 'CALCULATEINTEREST', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'POSTINTEREST_RECURRINGDEPOSITACCOUNT', 'RECURRINGDEPOSITACCOUNT', 'POSTINTEREST', 1); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'POSTINTEREST_RECURRINGDEPOSITACCOUNT_CHECKER', 'RECURRINGDEPOSITACCOUNT', 'POSTINTEREST', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'APPROVE_RECURRINGDEPOSITACCOUNT', 'RECURRINGDEPOSITACCOUNT', 'APPROVE', 1); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'REJECT_RECURRINGDEPOSITACCOUNT', 'RECURRINGDEPOSITACCOUNT', 'REJECT', 1); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'WITHDRAW_RECURRINGDEPOSITACCOUNT', 'RECURRINGDEPOSITACCOUNT', 'WITHDRAW', 1); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'APPROVALUNDO_RECURRINGDEPOSITACCOUNT', 'RECURRINGDEPOSITACCOUNT', 'APPROVALUNDO', 1); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'CLOSE_RECURRINGDEPOSITACCOUNT', 'RECURRINGDEPOSITACCOUNT', 'CLOSE', 1); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'APPROVE_RECURRINGDEPOSITACCOUNT_CHECKER', 'RECURRINGDEPOSITACCOUNT', 'APPROVE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'REJECT_RECURRINGDEPOSITACCOUNT_CHECKER', 'RECURRINGDEPOSITACCOUNT', 'REJECT', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'WITHDRAW_RECURRINGDEPOSITACCOUNT_CHECKER', 'RECURRINGDEPOSITACCOUNT', 'WITHDRAW', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'APPROVALUNDO_RECURRINGDEPOSITACCOUNT_CHECKER', 'RECURRINGDEPOSITACCOUNT', 'APPROVALUNDO', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'CLOSE_RECURRINGDEPOSITACCOUNT_CHECKER', 'RECURRINGDEPOSITACCOUNT', 'CLOSE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'UNDOTRANSACTION_RECURRINGDEPOSITACCOUNT', 'RECURRINGDEPOSITACCOUNT', 'UNDOTRANSACTION', 1); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'UNDOTRANSACTION_RECURRINGDEPOSITACCOUNT_CHECKER', 'RECURRINGDEPOSITACCOUNT', 'UNDOTRANSACTION', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'transaction_savings', 'ADJUSTTRANSACTION_RECURRINGDEPOSITACCOUNT', 'RECURRINGDEPOSITACCOUNT', 'ADJUSTTRANSACTION', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('transaction_savings', 'PREMATURECLOSE_FIXEDDEPOSITACCOUNT_CHECKER', 'FIXEDDEPOSITACCOUNT', 'PREMATURECLOSE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`) VALUES ('transaction_savings', 'PREMATURECLOSE_FIXEDDEPOSITACCOUNT', 'FIXEDDEPOSITACCOUNT', 'PREMATURECLOSE'); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('transaction_savings', 'PREMATURECLOSE_RECURRINGDEPOSITACCOUNT_CHECKER', 'RECURRINGDEPOSITACCOUNT', 'PREMATURECLOSE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`) VALUES ('transaction_savings', 'PREMATURECLOSE_RECURRINGDEPOSITACCOUNT', 'RECURRINGDEPOSITACCOUNT', 'PREMATURECLOSE'); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java index 65dbe71d7..6db12f1d7 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java @@ -5,6 +5,8 @@ */ package org.mifosplatform.commands.domain; +import org.mifosplatform.portfolio.savings.DepositAccountType; + public class CommandWrapper { private final Long commandId; @@ -23,6 +25,11 @@ public class CommandWrapper { private final String json; private final String transactionId; private final Long productId; + + @SuppressWarnings("unused") + private Long templateId; + private Long interestRatechartId; + private DepositAccountType depositAccountType; public static CommandWrapper wrap(final String actionName, final String entityName, final Long resourceId, final Long subresourceId) { return new CommandWrapper(null, actionName, entityName, resourceId, subresourceId, null, null); @@ -61,7 +68,9 @@ public class CommandWrapper { public CommandWrapper(final Long officeId, final Long groupId, final Long clientId, final Long loanId, final Long savingsId, final String actionName, final String entityName, final Long entityId, final Long subentityId, final String href, - final String json, final String transactionId, final Long productId) { + final String json, final String transactionId, final Long productId, final Long templateId, Long interestRatechartId, + DepositAccountType depositAccountType) { + this.commandId = null; this.officeId = officeId; this.groupId = groupId; @@ -77,6 +86,9 @@ public class CommandWrapper { this.json = json; this.transactionId = transactionId; this.productId = productId; + this.templateId = templateId; + this.interestRatechartId = interestRatechartId; + this.depositAccountType = depositAccountType; } private CommandWrapper(final Long commandId, final String actionName, final String entityName, final Long resourceId, @@ -555,6 +567,14 @@ public class CommandWrapper { return this.actionName.equalsIgnoreCase("PAY") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNTCHARGE"); } + public boolean isInterestRateChartResource() { + return this.entityName.equalsIgnoreCase("INTERESTRATECHART"); + } + + public boolean isInterestRateChartSlabResource() { + return this.entityName.equalsIgnoreCase("CHARTSLAB"); + } + public boolean isCalendarResource() { return this.entityName.equalsIgnoreCase("CALENDAR"); } @@ -692,4 +712,84 @@ public class CommandWrapper { public boolean isCacheResource() { return this.entityName.equalsIgnoreCase("CACHE"); } + + public Long interestRatechartId() { + return this.interestRatechartId; + } + + // Begin - Deposit product + public boolean isFixedDepositProductResource() { + return this.entityName.equalsIgnoreCase("FIXEDDEPOSITPRODUCT"); + } + + public boolean isRecurringDepositProductResource() { + return this.entityName.equalsIgnoreCase("RECURRINGDEPOSITPRODUCT"); + } + + // End - Deposit product + + // Begin - Deposit accounts + public boolean isDepositAccountResource() { + return this.entityName.equalsIgnoreCase("FIXEDDEPOSITACCOUNT") || this.entityName.equalsIgnoreCase("RECURRINGDEPOSITACCOUNT"); + } + + public boolean isRejectionOfDepositAccountApplication() { + return this.actionName.equalsIgnoreCase("REJECT") && isDepositAccountResource(); + } + + public boolean isWithdrawFromDepositAccountApplicationByApplicant() { + return this.actionName.equalsIgnoreCase("WITHDRAW") && isDepositAccountResource(); + } + + public boolean isApprovalOfDepositAccountApplication() { + return this.actionName.equalsIgnoreCase("APPROVE") && isDepositAccountResource(); + } + + public boolean isUndoApprovalOfDepositAccountApplication() { + return this.actionName.equalsIgnoreCase("APPROVALUNDO") && isDepositAccountResource(); + } + + //Account transaction actions + + public boolean isDepositAccountDeposit() { + return this.actionName.equalsIgnoreCase("DEPOSIT") && isDepositAccountResource(); + } + + public boolean isDepositAccountWithdrawal() { + return this.actionName.equalsIgnoreCase("WITHDRAWAL") && isDepositAccountResource(); + } + + public boolean isDepositAccountActivation() { + return this.actionName.equalsIgnoreCase("ACTIVATE") && isDepositAccountResource(); + } + + public boolean isDepositAccountInterestCalculation() { + return this.actionName.equalsIgnoreCase("CALCULATEINTEREST") && isDepositAccountResource(); + } + + public boolean isDepositAccountInterestPosting() { + return this.actionName.equalsIgnoreCase("POSTINTEREST") && isDepositAccountResource(); + } + + public boolean isDepositAccountUndoTransaction() { + return this.actionName.equalsIgnoreCase("UNDOTRANSACTION") && isDepositAccountResource(); + } + + public boolean isAdjustDeopsitAccountTransaction() { + return this.actionName.equalsIgnoreCase("ADJUSTTRANSACTION") && isDepositAccountResource(); + } + + public boolean isDepositAccountClose() { + return this.actionName.equalsIgnoreCase("CLOSE") && isDepositAccountResource(); + } + + public boolean isDepositAccountPrematureClose() { + return this.actionName.equalsIgnoreCase("PREMATURECLOSE") && isDepositAccountResource(); + } + + // End - Deposit accounts + + public DepositAccountType depositAccountType(){ + return this.depositAccountType; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java index ce450d99a..b43031f3c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java @@ -6,6 +6,7 @@ package org.mifosplatform.commands.service; import org.mifosplatform.commands.domain.CommandWrapper; +import org.mifosplatform.portfolio.savings.DepositAccountType; public class CommandWrapperBuilder { @@ -22,10 +23,14 @@ public class CommandWrapperBuilder { private String json = "{}"; private String transactionId; private Long productId; + private Long templateId; + private Long interestRatechartId; + private DepositAccountType depositAccountType; public CommandWrapper build() { return new CommandWrapper(this.officeId, this.groupId, this.clientId, this.loanId, this.savingsId, this.actionName, - this.entityName, this.entityId, this.subentityId, this.href, this.json, this.transactionId, this.productId); + this.entityName, this.entityId, this.subentityId, this.href, this.json, this.transactionId, this.productId, + this.templateId, this.interestRatechartId, this.depositAccountType); } public CommandWrapperBuilder withLoanId(final Long withLoanId) { @@ -1086,6 +1091,105 @@ public class CommandWrapperBuilder { return this; } + public CommandWrapperBuilder createFixedDepositProduct() { + this.actionName = "CREATE"; + this.entityName = "FIXEDDEPOSITPRODUCT"; + this.entityId = null; + this.href = "/fixeddepositproducts/template"; + return this; + } + + public CommandWrapperBuilder updateFixedDepositProduct(final Long productId) { + this.actionName = "UPDATE"; + this.entityName = "FIXEDDEPOSITPRODUCT"; + this.entityId = productId; + this.href = "/fixeddepositproducts/" + productId; + return this; + } + + public CommandWrapperBuilder deleteFixedDepositProduct(final Long productId) { + this.actionName = "DELETE"; + this.entityName = "FIXEDDEPOSITPRODUCT"; + this.entityId = productId; + this.href = "/fixeddepositproducts/" + productId; + return this; + } + + public CommandWrapperBuilder createRecurringDepositProduct() { + this.actionName = "CREATE"; + this.entityName = "RECURRINGDEPOSITPRODUCT"; + this.entityId = null; + this.href = "/recurringdepositproducts/template"; + return this; + } + + public CommandWrapperBuilder updateRecurringDepositProduct(final Long productId) { + this.actionName = "UPDATE"; + this.entityName = "RECURRINGDEPOSITPRODUCT"; + this.entityId = productId; + this.href = "/recurringdepositproducts/" + productId; + return this; + } + + public CommandWrapperBuilder deleteRecurringDepositProduct(final Long productId) { + this.actionName = "DELETE"; + this.entityName = "RECURRINGDEPOSITPRODUCT"; + this.entityId = productId; + this.href = "/recurringdepositproducts/" + productId; + return this; + } + + public CommandWrapperBuilder createInterestRateChart() { + this.actionName = "CREATE"; + this.entityName = "INTERESTRATECHART"; + this.entityId = null; + this.href = "/interestratechart/template"; + return this; + } + + public CommandWrapperBuilder updateInterestRateChart(final Long interestRateChartId) { + this.actionName = "UPDATE"; + this.entityName = "INTERESTRATECHART"; + this.entityId = interestRateChartId; + this.href = "/interestratechart/" + interestRateChartId; + return this; + } + + public CommandWrapperBuilder deleteInterestRateChart(final Long interestRateChartId) { + this.actionName = "DELETE"; + this.entityName = "INTERESTRATECHART"; + this.entityId = interestRateChartId; + this.href = "/interestratechart/" + interestRateChartId; + return this; + } + + public CommandWrapperBuilder createInterestRateChartSlab(final Long chartId) { + this.actionName = "CREATE"; + this.entityName = "CHARTSLAB"; + this.entityId = null; + this.interestRatechartId = chartId; + this.href = "/interestratechart/" + chartId + "/chartdetails/template"; + return this; + } + + public CommandWrapperBuilder updateInterestRateChartSlab(final Long chartId, final Long chartSlabId) { + this.actionName = "UPDATE"; + this.entityName = "CHARTSLAB"; + this.entityId = chartSlabId; + this.interestRatechartId = chartId; + this.href = "/interestratechart/" + chartId + "/chartdetails/" + chartSlabId; + return this; + } + + public CommandWrapperBuilder deleteInterestRateChartSlab(final Long chartId, final Long chartSlabId) { + this.actionName = "DELETE"; + this.entityName = "CHARTSLAB"; + this.entityId = chartSlabId; + this.interestRatechartId = chartId; + this.href = "/interestratechart/" + chartId + "/chartdetails/" + chartSlabId; + return this; + } + public CommandWrapperBuilder createCalendar(final CommandWrapper resourceDetails, final String supportedEntityType, final Long supportedEntityId) { this.actionName = "CREATE"; @@ -1567,4 +1671,306 @@ public class CommandWrapperBuilder { this.href = "/cache"; return this; } + + /** + * Deposit account mappings + */ + + public CommandWrapperBuilder createFixedDepositAccount() { + this.actionName = "CREATE"; + this.entityName = "FIXEDDEPOSITACCOUNT"; + this.entityId = null; + this.depositAccountType = DepositAccountType.FIXED_DEPOSIT; + this.href = "/fixeddepositaccounts/template"; + return this; + } + + public CommandWrapperBuilder updateFixedDepositAccount(final Long accountId) { + this.actionName = "UPDATE"; + this.entityName = "FIXEDDEPOSITACCOUNT"; + this.entityId = accountId; + this.depositAccountType = DepositAccountType.FIXED_DEPOSIT; + this.href = "/fixeddepositaccounts/" + accountId; + return this; + } + + public CommandWrapperBuilder deleteFixedDepositAccount(final Long accountId) { + this.actionName = "DELETE"; + this.entityName = "FIXEDDEPOSITACCOUNT"; + this.entityId = accountId; + this.depositAccountType = DepositAccountType.FIXED_DEPOSIT; + this.href = "/fixeddepositaccounts/" + accountId; + return this; + } + + public CommandWrapperBuilder rejectFixedDepositAccountApplication(final Long accountId) { + this.actionName = "REJECT"; + this.entityName = "FIXEDDEPOSITACCOUNT"; + this.entityId = accountId; + this.savingsId = accountId; + this.depositAccountType = DepositAccountType.FIXED_DEPOSIT; + this.href = "/fixeddepositaccounts/" + accountId + "?command=reject"; + return this; + } + + public CommandWrapperBuilder withdrawFixedDepositAccountApplication(final Long accountId) { + this.actionName = "WITHDRAW"; + this.entityName = "FIXEDDEPOSITACCOUNT"; + this.entityId = accountId; + this.savingsId = accountId; + this.depositAccountType = DepositAccountType.FIXED_DEPOSIT; + this.href = "/fixeddepositaccounts/" + accountId + "?command=withdrawnByApplicant"; + return this; + } + + public CommandWrapperBuilder approveFixedDepositAccountApplication(final Long accountId) { + this.actionName = "APPROVE"; + this.entityName = "FIXEDDEPOSITACCOUNT"; + this.entityId = accountId; + this.savingsId = accountId; + this.depositAccountType = DepositAccountType.FIXED_DEPOSIT; + this.href = "/fixeddepositaccounts/" + accountId + "?command=approve"; + return this; + } + + public CommandWrapperBuilder undoFixedDepositAccountApplication(final Long accountId) { + this.actionName = "APPROVALUNDO"; + this.entityName = "FIXEDDEPOSITACCOUNT"; + this.entityId = accountId; + this.savingsId = accountId; + this.depositAccountType = DepositAccountType.FIXED_DEPOSIT; + this.href = "/fixeddepositaccounts/" + accountId + "?command=undoapproval"; + return this; + } + + public CommandWrapperBuilder fixedDepositAccountActivation(final Long accountId) { + this.actionName = "ACTIVATE"; + this.entityName = "FIXEDDEPOSITACCOUNT"; + this.savingsId = accountId; + this.entityId = accountId; + this.depositAccountType = DepositAccountType.FIXED_DEPOSIT; + this.href = "/fixeddepositaccounts/" + accountId + "?command=activate"; + return this; + } + + public CommandWrapperBuilder closeFixedDepositAccount(final Long accountId) { + this.actionName = "CLOSE"; + this.entityName = "FIXEDDEPOSITACCOUNT"; + this.entityId = accountId; + this.savingsId = accountId; + this.depositAccountType = DepositAccountType.FIXED_DEPOSIT; + this.href = "/fixeddepositaccounts/" + accountId + "?command=close"; + return this; + } + + public CommandWrapperBuilder prematureCloseFixedDepositAccount(final Long accountId) { + this.actionName = "PREMATURECLOSE"; + this.entityName = "FIXEDDEPOSITACCOUNT"; + this.entityId = accountId; + this.savingsId = accountId; + this.depositAccountType = DepositAccountType.FIXED_DEPOSIT; + this.href = "/fixeddepositaccounts/" + accountId + "?command=prematureClose"; + return this; + } + + public CommandWrapperBuilder fixedDepositAccountInterestCalculation(final Long accountId) { + this.actionName = "CALCULATEINTEREST"; + this.entityName = "FIXEDDEPOSITACCOUNT"; + this.savingsId = accountId; + this.entityId = accountId; + this.depositAccountType = DepositAccountType.FIXED_DEPOSIT; + this.href = "/fixeddepositaccounts/" + accountId + "?command=calculateInterest"; + return this; + } + + public CommandWrapperBuilder fixedDepositAccountInterestPosting(final Long accountId) { + this.actionName = "POSTINTEREST"; + this.entityName = "FIXEDDEPOSITACCOUNT"; + this.savingsId = accountId; + this.entityId = accountId; + this.depositAccountType = DepositAccountType.FIXED_DEPOSIT; + this.href = "/fixeddepositaccounts/" + accountId + "?command=postInterest"; + return this; + } + + public CommandWrapperBuilder fixedDepositAccountDeposit(final Long accountId) { + this.actionName = "DEPOSIT"; + this.entityName = "FIXEDDEPOSITACCOUNT"; + this.savingsId = accountId; + this.depositAccountType = DepositAccountType.FIXED_DEPOSIT; + this.entityId = accountId; + this.href = "/fixeddepositaccounts/" + accountId + "/transactions?command=deposit"; + return this; + } + + public CommandWrapperBuilder fixedDepositAccountWithdrawal(final Long accountId) { + this.actionName = "WITHDRAWAL"; + this.entityName = "FIXEDDEPOSITACCOUNT"; + this.savingsId = accountId; + this.depositAccountType = DepositAccountType.FIXED_DEPOSIT; + this.entityId = accountId; + this.href = "/fixeddepositaccounts/" + accountId + "/transactions?command=withdrawal"; + return this; + } + + public CommandWrapperBuilder createRecurringDepositAccount() { + this.actionName = "CREATE"; + this.entityName = "RECURRINGDEPOSITACCOUNT"; + this.entityId = null; + this.depositAccountType = DepositAccountType.RECURRING_DEPOSIT; + this.href = "/recurringdepositaccounts/template"; + return this; + } + + public CommandWrapperBuilder updateRecurringDepositAccount(final Long accountId) { + this.actionName = "UPDATE"; + this.entityName = "RECURRINGDEPOSITACCOUNT"; + this.entityId = accountId; + this.depositAccountType = DepositAccountType.RECURRING_DEPOSIT; + this.href = "/recurringdepositaccounts/" + accountId; + return this; + } + + public CommandWrapperBuilder recurringAccountDeposit(final Long accountId) { + this.actionName = "DEPOSIT"; + this.entityName = "RECURRINGDEPOSITACCOUNT"; + this.savingsId = accountId; + this.depositAccountType = DepositAccountType.RECURRING_DEPOSIT; + this.entityId = accountId; + this.href = "/recurringdepositaccounts/" + accountId + "/transactions?command=deposit"; + return this; + } + + public CommandWrapperBuilder recurringAccountWithdrawal(final Long accountId) { + this.actionName = "WITHDRAWAL"; + this.entityName = "RECURRINGDEPOSITACCOUNT"; + this.savingsId = accountId; + this.depositAccountType = DepositAccountType.RECURRING_DEPOSIT; + this.entityId = accountId; + this.href = "/recurringdepositaccounts/" + accountId + "/transactions?command=withdrawal"; + return this; + } + + public CommandWrapperBuilder adjustRecurringAccountTransaction(final Long accountId, final Long transactionId) { + this.actionName = "ADJUSTTRANSACTION"; + this.entityName = "RECURRINGDEPOSITACCOUNT"; + this.savingsId = accountId; + this.entityId = accountId; + this.depositAccountType = DepositAccountType.RECURRING_DEPOSIT; + this.subentityId = transactionId; + this.transactionId = transactionId.toString(); + this.href = "/recurringdepositaccounts/" + accountId + "/transactions/" + transactionId + "?command=modify"; + return this; + } + + public CommandWrapperBuilder undoRecurringAccountTransaction(final Long accountId, final Long transactionId) { + this.actionName = "UNDOTRANSACTION"; + this.entityName = "RECURRINGDEPOSITACCOUNT"; + this.savingsId = accountId; + this.entityId = accountId; + this.depositAccountType = DepositAccountType.RECURRING_DEPOSIT; + this.subentityId = transactionId; + this.transactionId = transactionId.toString(); + this.href = "/recurringdepositaccounts/" + accountId + "/transactions/" + transactionId + "?command=undo"; + return this; + } + + public CommandWrapperBuilder deleteRecurringDepositAccount(final Long accountId) { + this.actionName = "DELETE"; + this.entityName = "RECURRINGDEPOSITACCOUNT"; + this.entityId = accountId; + this.depositAccountType = DepositAccountType.RECURRING_DEPOSIT; + this.href = "/recurringdepositaccounts/" + accountId; + return this; + } + + public CommandWrapperBuilder rejectRecurringDepositAccountApplication(final Long accountId) { + this.actionName = "REJECT"; + this.entityName = "RECURRINGDEPOSITACCOUNT"; + this.entityId = accountId; + this.savingsId = accountId; + this.depositAccountType = DepositAccountType.RECURRING_DEPOSIT; + this.href = "/recurringdepositaccounts/" + accountId + "?command=reject"; + return this; + } + + public CommandWrapperBuilder withdrawRecurringDepositAccountApplication(final Long accountId) { + this.actionName = "WITHDRAW"; + this.entityName = "RECURRINGDEPOSITACCOUNT"; + this.entityId = accountId; + this.savingsId = accountId; + this.depositAccountType = DepositAccountType.RECURRING_DEPOSIT; + this.href = "/recurringdepositaccounts/" + accountId + "?command=withdrawnByApplicant"; + return this; + } + + public CommandWrapperBuilder approveRecurringDepositAccountApplication(final Long accountId) { + this.actionName = "APPROVE"; + this.entityName = "RECURRINGDEPOSITACCOUNT"; + this.entityId = accountId; + this.savingsId = accountId; + this.depositAccountType = DepositAccountType.RECURRING_DEPOSIT; + this.href = "/recurringdepositaccounts/" + accountId + "?command=approve"; + return this; + } + + public CommandWrapperBuilder undoRecurringDepositAccountApplication(final Long accountId) { + this.actionName = "APPROVALUNDO"; + this.entityName = "RECURRINGDEPOSITACCOUNT"; + this.entityId = accountId; + this.savingsId = accountId; + this.depositAccountType = DepositAccountType.RECURRING_DEPOSIT; + this.href = "/recurringdepositaccounts/" + accountId + "?command=undoapproval"; + return this; + } + + public CommandWrapperBuilder recurringDepositAccountActivation(final Long accountId) { + this.actionName = "ACTIVATE"; + this.entityName = "RECURRINGDEPOSITACCOUNT"; + this.savingsId = accountId; + this.entityId = accountId; + this.depositAccountType = DepositAccountType.RECURRING_DEPOSIT; + this.href = "/recurringdepositaccounts/" + accountId + "?command=activate"; + return this; + } + + public CommandWrapperBuilder closeRecurringDepositAccount(final Long accountId) { + this.actionName = "CLOSE"; + this.entityName = "RECURRINGDEPOSITACCOUNT"; + this.entityId = accountId; + this.savingsId = accountId; + this.depositAccountType = DepositAccountType.RECURRING_DEPOSIT; + this.href = "/recurringdepositaccounts/" + accountId + "?command=close"; + return this; + } + + public CommandWrapperBuilder prematureCloseRecurringDepositAccount(final Long accountId) { + this.actionName = "PREMATURECLOSE"; + this.entityName = "RECURRINGDEPOSITACCOUNT"; + this.entityId = accountId; + this.savingsId = accountId; + this.depositAccountType = DepositAccountType.RECURRING_DEPOSIT; + this.href = "/recurringdepositaccounts/" + accountId + "?command=prematureClose"; + return this; + } + + public CommandWrapperBuilder recurringDepositAccountInterestCalculation(final Long accountId) { + this.actionName = "CALCULATEINTEREST"; + this.entityName = "RECURRINGDEPOSITACCOUNT"; + this.savingsId = accountId; + this.entityId = accountId; + this.depositAccountType = DepositAccountType.RECURRING_DEPOSIT; + this.href = "/recurringdepositaccounts/" + accountId + "?command=calculateInterest"; + return this; + } + + public CommandWrapperBuilder recurringDepositAccountInterestPosting(final Long accountId) { + this.actionName = "POSTINTEREST"; + this.entityName = "RECURRINGDEPOSITACCOUNT"; + this.savingsId = accountId; + this.entityId = accountId; + this.depositAccountType = DepositAccountType.RECURRING_DEPOSIT; + this.href = "/recurringdepositaccounts/" + accountId + "?command=postInterest"; + return this; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/PortfolioCommandSourceWritePlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/PortfolioCommandSourceWritePlatformServiceImpl.java index c49666056..988ef29e6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/PortfolioCommandSourceWritePlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/PortfolioCommandSourceWritePlatformServiceImpl.java @@ -66,7 +66,8 @@ public class PortfolioCommandSourceWritePlatformServiceImpl implements Portfolio final JsonElement parsedCommand = this.fromApiJsonHelper.parse(json); final JsonCommand command = JsonCommand.from(json, parsedCommand, this.fromApiJsonHelper, wrapper.getEntityName(), wrapper.getEntityId(), wrapper.getSubentityId(), wrapper.getGroupId(), wrapper.getClientId(), wrapper.getLoanId(), - wrapper.getSavingsId(), wrapper.getTransactionId(), wrapper.getHref(), wrapper.getProductId()); + wrapper.getSavingsId(), wrapper.getTransactionId(), wrapper.getHref(), wrapper.getProductId(), + wrapper.interestRatechartId(), wrapper.depositAccountType()); result = this.processAndLogCommandService.processAndLogCommand(wrapper, command, isApprovedByChecker); } catch (final RollbackTransactionAsCommandIsNotApprovedByCheckerException e) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java index f70c5f308..b6030e6de 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java @@ -422,6 +422,26 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer } else { throw new UnsupportedCommandException(wrapper.commandName()); } + } else if (wrapper.isFixedDepositProductResource()) { + if (wrapper.isCreate()) { + handler = this.applicationContext.getBean("createFixedDepositProductCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isUpdate()) { + handler = this.applicationContext.getBean("updateFixedDepositProductCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isDelete()) { + handler = this.applicationContext.getBean("deleteFixedDepositProductCommandHandler", NewCommandSourceHandler.class); + } else { + throw new UnsupportedCommandException(wrapper.commandName()); + } + } else if (wrapper.isRecurringDepositProductResource()) { + if (wrapper.isCreate()) { + handler = this.applicationContext.getBean("createRecurringDepositProductCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isUpdate()) { + handler = this.applicationContext.getBean("updateRecurringDepositProductCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isDelete()) { + handler = this.applicationContext.getBean("deleteRecurringDepositProductCommandHandler", NewCommandSourceHandler.class); + } else { + throw new UnsupportedCommandException(wrapper.commandName()); + } } else if (wrapper.isSavingsAccountResource()) { if (wrapper.isCreate()) { handler = this.applicationContext @@ -479,6 +499,66 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer } else { throw new UnsupportedCommandException(wrapper.commandName()); } + } else if (wrapper.isDepositAccountResource()) { + if (wrapper.isCreate()) { + handler = this.applicationContext + .getBean("depositAccountApplicationSubmittalCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isUpdate()) { + handler = this.applicationContext.getBean("depositAccountApplicationModificationCommandHandler", + NewCommandSourceHandler.class); + } else if (wrapper.isDelete()) { + handler = this.applicationContext.getBean("depositAccountApplicationDeletionCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isRejectionOfDepositAccountApplication()) { + handler = this.applicationContext.getBean("depositAccountApplicationRejectedCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isWithdrawFromDepositAccountApplicationByApplicant()) { + handler = this.applicationContext.getBean("depositAccountApplicationWithdrawnByApplicantCommandHandler", + NewCommandSourceHandler.class); + } else if (wrapper.isApprovalOfDepositAccountApplication()) { + handler = this.applicationContext.getBean("depositAccountApplicationApprovalCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isUndoApprovalOfDepositAccountApplication()) { + handler = this.applicationContext.getBean("depositAccountApplicationApprovalUndoCommandHandler", + NewCommandSourceHandler.class); + } else if (wrapper.isDepositAccountDeposit()) { + handler = this.applicationContext.getBean("depositAccountDepositCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isDepositAccountWithdrawal()) { + handler = this.applicationContext.getBean("withdrawDepositAccountCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isDepositAccountActivation()) { + handler = this.applicationContext.getBean("activateDepositAccountCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isDepositAccountInterestCalculation()) { + handler = this.applicationContext.getBean("calculateInterestDepositAccountCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isDepositAccountInterestPosting()) { + handler = this.applicationContext.getBean("postInterestDepositAccountCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isDepositAccountUndoTransaction()) { + handler = this.applicationContext.getBean("undoTransactionDepositAccountCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isAdjustDeopsitAccountTransaction()) { + handler = this.applicationContext.getBean("depositTransactionAdjustmentCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isDepositAccountClose()) { + handler = this.applicationContext.getBean("closeDepositAccountCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isDepositAccountPrematureClose()) { + handler = this.applicationContext.getBean("prematureCloseDepositAccountCommandHandler", NewCommandSourceHandler.class); + } else { + throw new UnsupportedCommandException(wrapper.commandName()); + } + } else if (wrapper.isInterestRateChartResource()) { + if (wrapper.isCreate()) { + handler = this.applicationContext.getBean("createInterestRateChartCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isUpdate()) { + handler = this.applicationContext.getBean("updateInterestRateChartCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isDelete()) { + handler = this.applicationContext.getBean("deleteInterestRateChartCommandHandler", NewCommandSourceHandler.class); + } else { + throw new UnsupportedCommandException(wrapper.commandName()); + } + } else if (wrapper.isInterestRateChartSlabResource()) { + if (wrapper.isCreate()) { + handler = this.applicationContext.getBean("createInterestRateChartSlabCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isUpdate()) { + handler = this.applicationContext.getBean("updateInterestRateChartSlabCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isDelete()) { + handler = this.applicationContext.getBean("deleteInterestRateChartSlabCommandHandler", NewCommandSourceHandler.class); + } else { + throw new UnsupportedCommandException(wrapper.commandName()); + } } else if (wrapper.isCalendarResource()) { if (wrapper.isCreate()) { handler = this.applicationContext.getBean("createCalendarCommandHandler", NewCommandSourceHandler.class); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/JsonCommand.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/JsonCommand.java index 0bc3c86da..784645361 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/JsonCommand.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/JsonCommand.java @@ -21,6 +21,7 @@ import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.infrastructure.security.domain.BasicPasswordEncodablePlatformUser; import org.mifosplatform.infrastructure.security.domain.PlatformUser; import org.mifosplatform.infrastructure.security.service.PlatformPasswordEncoder; +import org.mifosplatform.portfolio.savings.DepositAccountType; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -48,33 +49,50 @@ public final class JsonCommand { private final String transactionId; private final String url; private final Long productId; + private final Long interestRateChartId; + private final DepositAccountType depositAccountType; public static JsonCommand from(final String jsonCommand, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper, final String entityName, final Long resourceId, final Long subresourceId, final Long groupId, final Long clientId, - final Long loanId, final Long savingsId, final String transactionId, final String url, final Long productId) { + final Long loanId, final Long savingsId, final String transactionId, final String url, final Long productId, + final Long interestRateChartId, final DepositAccountType depositAccountType) { return new JsonCommand(null, jsonCommand, parsedCommand, fromApiJsonHelper, entityName, resourceId, subresourceId, groupId, - clientId, loanId, savingsId, transactionId, url, productId); + clientId, loanId, savingsId, transactionId, url, productId, interestRateChartId, depositAccountType); + } public static JsonCommand fromExistingCommand(final Long commandId, final String jsonCommand, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper, final String entityName, final Long resourceId, final Long subresourceId, final String url, final Long productId) { + final Long interestRateChartId = null; + final DepositAccountType depositAccountType = null; return new JsonCommand(commandId, jsonCommand, parsedCommand, fromApiJsonHelper, entityName, resourceId, subresourceId, null, null, - null, null, null, url, productId); + null, null, null, url, productId, interestRateChartId, depositAccountType); } public static JsonCommand fromExistingCommand(final Long commandId, final String jsonCommand, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper, final String entityName, final Long resourceId, final Long subresourceId, final Long groupId, final Long clientId, final Long loanId, final Long savingsId, final String transactionId, final String url, final Long productId) { + final Long interestRateChartId = null; + final DepositAccountType depositAccountType = null; return new JsonCommand(commandId, jsonCommand, parsedCommand, fromApiJsonHelper, entityName, resourceId, subresourceId, groupId, - clientId, loanId, savingsId, transactionId, url, productId); + clientId, loanId, savingsId, transactionId, url, productId, interestRateChartId, depositAccountType); + + } + + public static JsonCommand fromExistingCommand(JsonCommand command, final JsonElement parsedCommand) { + final String jsonCommand = command.fromApiJsonHelper.toJson(parsedCommand); + return new JsonCommand(command.commandId, jsonCommand, parsedCommand, command.fromApiJsonHelper, command.entityName, + command.resourceId, command.subresourceId, command.groupId, command.clientId, command.loanId, command.savingsId, + command.transactionId, command.url, command.productId, command.interestRateChartId, command.depositAccountType); } public JsonCommand(final Long commandId, final String jsonCommand, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper, final String entityName, final Long resourceId, final Long subresourceId, final Long groupId, final Long clientId, final Long loanId, final Long savingsId, final String transactionId, final String url, - final Long productId) { + final Long productId, final Long interestRateChartId, final DepositAccountType depositAccountType) { + this.commandId = commandId; this.jsonCommand = jsonCommand; this.parsedCommand = parsedCommand; @@ -89,6 +107,8 @@ public final class JsonCommand { this.transactionId = transactionId; this.url = url; this.productId = productId; + this.interestRateChartId = interestRateChartId; + this.depositAccountType = depositAccountType; } public String json() { @@ -483,4 +503,12 @@ public final class JsonCommand { public void checkForUnsupportedParameters(final Type typeOfMap, final String json, final Set requestDataParameters) { this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, requestDataParameters); } + + public Long interestRateChartId() { + return this.interestRateChartId; + } + + public DepositAccountType depositAccounttype() { + return this.depositAccountType; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java index 933d05b28..fd41aa592 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java @@ -82,20 +82,20 @@ public class DataValidatorBuilder { return this; } - public void failWithCode(final String errorCode) { + public void failWithCode(final String errorCode, final Object... defaultUserMessageArgs) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".").append(errorCode); final StringBuilder defaultEnglishMessage = new StringBuilder("Failed data validation due to: ").append(errorCode).append("."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), - this.parameter, this.value); + this.parameter, this.value, defaultUserMessageArgs); this.dataValidationErrors.add(error); } - public void failWithCodeNoParameterAddedToErrorCode(final String errorCode) { + public void failWithCodeNoParameterAddedToErrorCode(final String errorCode, final Object... defaultUserMessageArgs) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".").append(errorCode); final StringBuilder defaultEnglishMessage = new StringBuilder("Failed data validation due to: ").append(errorCode).append("."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), - this.parameter, this.value); + this.parameter, this.value, defaultUserMessageArgs); this.dataValidationErrors.add(error); } @@ -383,7 +383,25 @@ public class DataValidatorBuilder { } return this; } + + public DataValidatorBuilder integerEqualToOrGreaterThanNumber(Integer number) { + if (this.value == null && this.ignoreNullValue) { return this; } + if (this.value != null) { + final Integer intValue = Integer.valueOf(this.value.toString()); + if (intValue < number) { + final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") + .append(this.parameter).append(".not.equal.to.or.greater.than.specified.number"); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter).append( + " must be equal to or greater than").append(number); + final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), + defaultEnglishMessage.toString(), this.parameter, intValue, number); + this.dataValidationErrors.add(error); + } + } + return this; + } + public DataValidatorBuilder integerSameAsNumber(Integer number) { if (this.value == null && this.ignoreNullValue) { return this; } @@ -785,5 +803,23 @@ public class DataValidatorBuilder { } return this; } + + public DataValidatorBuilder validateDateBefore(final LocalDate date) { + if (this.value == null && this.ignoreNullValue) { return this; } + + if (this.value != null && date != null) { + final LocalDate dateVal = (LocalDate) this.value; + if (date.isBefore(dateVal)) { + final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") + .append(this.parameter).append(".is.greater.than.date"); + final StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(this.parameter) + .append(" must be less than provided date").append(date); + final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), + defaultEnglishMessage.toString(), this.parameter, dateVal, date); + this.dataValidationErrors.add(error); + } + } + return this; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/EnumOptionData.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/EnumOptionData.java index f169603cd..c31c488d0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/EnumOptionData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/EnumOptionData.java @@ -14,7 +14,6 @@ public class EnumOptionData { private final Long id; private final String code; - @SuppressWarnings("unused") private final String value; public EnumOptionData(final Long id, final String code, final String value) { @@ -30,4 +29,10 @@ public class EnumOptionData { public String getCode() { return this.code; } + + public String getValue() { + return this.value; + } + + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/PaginationParameters.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/PaginationParameters.java index c4eff368a..ec1b9c40e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/PaginationParameters.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/PaginationParameters.java @@ -112,4 +112,16 @@ public class PaginationParameters { } return sql.toString(); } + + public String paginationSql(){ + final StringBuilder sqlBuilder = new StringBuilder(50); + if (this.isOrderByRequested()) { + sqlBuilder.append(' ').append(this.orderBySql()); + } + if (this.isLimited()) { + sqlBuilder.append(' ').append(this.limitSql()); + } + + return sqlBuilder.toString(); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/domain/JdbcSupport.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/domain/JdbcSupport.java index dc5ae9378..67ffee5af 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/domain/JdbcSupport.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/domain/JdbcSupport.java @@ -51,6 +51,11 @@ public class JdbcSupport { final Integer value = (Integer) JdbcUtils.getResultSetValue(rs, rs.findColumn(columnName), Integer.class); return defaultToNullIfZero(value); } + + public static Long getLongDefaultToNullIfZero(final ResultSet rs, final String columnName) throws SQLException { + final Long value = (Long) JdbcUtils.getResultSetValue(rs, rs.findColumn(columnName), Long.class); + return defaultToNullIfZero(value); + } private static Integer defaultToNullIfZero(final Integer value) { Integer result = value; @@ -59,6 +64,14 @@ public class JdbcSupport { } return result; } + + private static Long defaultToNullIfZero(final Long value) { + Long result = value; + if (result != null && Long.valueOf(0).equals(value)) { + result = null; + } + return result; + } public static BigDecimal getBigDecimalDefaultToZeroIfNull(final ResultSet rs, final String columnName) throws SQLException { final BigDecimal value = rs.getBigDecimal(columnName); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java index 7698be461..db614f928 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java @@ -14,7 +14,8 @@ public enum JobName { APPLY_CHARGE_TO_OVERDUE_LOAN_INSTALLMENT("Apply penalty to overdue loans"), EXECUTE_STANDING_INSTRUCTIONS("Execute Standing Instruction"), ADD_ACCRUAL_ENTRIES("Add Accrual Transactions"), - UPDATE_NPA("Update Non Performing Assets"); + UPDATE_NPA("Update Non Performing Assets"), + UPDATE_DEPOSITS_ACCOUNT_MATURITY_DETAILS("Update Deposit Accounts Maturity details"); private final String name; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/SavingsAccountSummaryData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/SavingsAccountSummaryData.java index b5731bca5..db0063982 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/SavingsAccountSummaryData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/SavingsAccountSummaryData.java @@ -26,12 +26,16 @@ public class SavingsAccountSummaryData { private final SavingsAccountStatusEnumData status; private final CurrencyData currency; private final BigDecimal accountBalance; + //differentiate Individual, JLG or Group account private final EnumOptionData accountType; private final SavingsAccountApplicationTimelineData timeline; + //differentiate deposit accounts Savings, FD and RD accounts + private final EnumOptionData depositType; + public SavingsAccountSummaryData(final Long id, final String accountNo, final String externalId, final Long productId, final String productName, final SavingsAccountStatusEnumData status, final CurrencyData currency, - final BigDecimal accountBalance, final EnumOptionData accountType, final SavingsAccountApplicationTimelineData timeline) { + final BigDecimal accountBalance, final EnumOptionData accountType, final SavingsAccountApplicationTimelineData timeline, final EnumOptionData depositType) { this.id = id; this.accountNo = accountNo; this.externalId = externalId; @@ -42,5 +46,6 @@ public class SavingsAccountSummaryData { this.accountBalance = accountBalance; this.accountType = accountType; this.timeline = timeline; + this.depositType = depositType; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java index 7ce3d5d0a..1e5d64347 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java @@ -141,7 +141,8 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou .append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMultiplesOf, "); accountsSummary.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); accountsSummary.append("curr.display_symbol as currencyDisplaySymbol, "); - accountsSummary.append("sa.product_id as productId, p.name as productName "); + accountsSummary.append("sa.product_id as productId, p.name as productName, "); + accountsSummary.append("sa.deposit_type_enum as depositType "); accountsSummary.append("from m_savings_account sa "); accountsSummary.append("join m_savings_product as p on p.id = sa.product_id "); accountsSummary.append("join m_currency curr on curr.code = sa.currency_code "); @@ -172,7 +173,9 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou final SavingsAccountStatusEnumData status = SavingsEnumerations.status(statusId); final Integer accountType = JdbcSupport.getInteger(rs, "accountType"); final EnumOptionData accountTypeData = AccountEnumerations.loanType(accountType); - + final Integer depositTypeId = JdbcSupport.getInteger(rs, "depositType"); + final EnumOptionData depositTypeData = SavingsEnumerations.depositType(depositTypeId); + final String currencyCode = rs.getString("currencyCode"); final String currencyName = rs.getString("currencyName"); final String currencyNameCode = rs.getString("currencyNameCode"); @@ -220,7 +223,7 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou closedByLastname); return new SavingsAccountSummaryData(id, accountNo, externalId, productId, productName, status, currency, accountBalance, - accountTypeData, timeline); + accountTypeData, timeline, depositTypeData); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientApiConstants.java index e797a8ecc..c4e519ccd 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientApiConstants.java @@ -15,7 +15,7 @@ public class ClientApiConstants { public static final String CLIENT_RESOURCE_NAME = "client"; public static final String CLIENT_CLOSURE_REASON = "ClientClosureReason"; - + public static final String GENDER = "Gender"; // general public static final String localeParamName = "locale"; public static final String dateFormatParamName = "dateFormat"; @@ -40,6 +40,9 @@ public class ClientApiConstants { public static final String submittedOnDateParamName = "submittedOnDate"; public static final String savingsProductIdParamName = "savingsProductId"; public static final String savingsAccountIdParamName = "savingsAccountId"; + public static final String dateOfBirthParamName = "dateOfBirth"; + public static final String genderIdParamName = "genderId"; + public static final String genderParamName = "gender"; // response parameters public static final String statusParamName = "status"; public static final String hierarchyParamName = "hierarchy"; @@ -64,11 +67,12 @@ public class ClientApiConstants { public static final Set CLIENT_CREATE_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, groupIdParamName, accountNoParamName, externalIdParamName, mobileNoParamName, firstnameParamName, middlenameParamName, lastnameParamName, fullnameParamName, officeIdParamName, activeParamName, activationDateParamName, - staffIdParamName, submittedOnDateParamName, savingsProductIdParamName)); + staffIdParamName, submittedOnDateParamName, savingsProductIdParamName, dateOfBirthParamName, genderIdParamName)); public static final Set CLIENT_UPDATE_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, accountNoParamName, externalIdParamName, mobileNoParamName, firstnameParamName, middlenameParamName, - lastnameParamName, fullnameParamName, activeParamName, activationDateParamName, staffIdParamName, savingsProductIdParamName)); + lastnameParamName, fullnameParamName, activeParamName, activationDateParamName, staffIdParamName, savingsProductIdParamName, + dateOfBirthParamName, genderIdParamName)); /** * These parameters will match the class level parameters of @@ -79,7 +83,8 @@ public class ClientApiConstants { externalIdParamName, statusParamName, activeParamName, activationDateParamName, firstnameParamName, middlenameParamName, lastnameParamName, fullnameParamName, displayNameParamName, mobileNoParamName, officeIdParamName, officeNameParamName, transferToOfficeIdParamName, transferToOfficeNameParamName, hierarchyParamName, imageIdParamName, imagePresentParamName, - staffIdParamName, staffNameParamName, timelineParamName, groupsParamName, officeOptionsParamName, staffOptionsParamName)); + staffIdParamName, staffNameParamName, timelineParamName, groupsParamName, officeOptionsParamName, staffOptionsParamName, + dateOfBirthParamName, genderParamName)); public static final Set ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, activationDateParamName)); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java index 0fa0f6728..bc04f00a9 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java @@ -40,6 +40,8 @@ final public class ClientData implements Comparable { private final String fullname; private final String displayName; private final String mobileNo; + private final LocalDate dateOfBirth; + private final CodeValueData gender; private final Long officeId; private final String officeName; @@ -67,12 +69,15 @@ final public class ClientData implements Comparable { private final Collection closureReasons; private final Collection savingProductOptions; private final Collection savingAccountOptions; + @SuppressWarnings("unused") + private final Collection genderOptions; public static ClientData template(final Long officeId, final LocalDate joinedDate, final Collection officeOptions, final Collection staffOptions, final Collection closureReasons, - final Collection savingProductOptions) { - return new ClientData(null, null, officeId, null, null, null, null, null, null, null, null, null, null, null, joinedDate, null, - null, null, officeOptions, null, staffOptions, closureReasons, null, savingProductOptions, null, null, null, null); + final Collection genderOptions, final Collection savingProductOptions) { + return new ClientData(null, null, officeId, null, null, null, null, null, null, null, null, null, null, null, null, null, + joinedDate, null, null, null, officeOptions, null, staffOptions, closureReasons, genderOptions, null, savingProductOptions, null, null, + null, null); } @@ -81,9 +86,10 @@ final public class ClientData implements Comparable { return new ClientData(clientData.accountNo, clientData.status, clientData.officeId, clientData.officeName, clientData.transferToOfficeId, clientData.transferToOfficeName, clientData.id, clientData.firstname, clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.mobileNo, - clientData.activationDate, clientData.imageId, clientData.staffId, clientData.staffName, templateData.officeOptions, - clientData.groups, templateData.staffOptions, null, null, templateData.savingProductOptions, clientData.savingsProductId, - clientData.savingsProductName, clientData.savingsAccountId, clientData.savingAccountOptions); + clientData.dateOfBirth, clientData.gender, clientData.activationDate, clientData.imageId, clientData.staffId, + clientData.staffName, templateData.officeOptions, clientData.groups, templateData.staffOptions, null, null, null, + templateData.savingProductOptions, clientData.savingsProductId, clientData.savingsProductName, clientData.savingsAccountId, + clientData.savingAccountOptions); } @@ -93,9 +99,10 @@ final public class ClientData implements Comparable { return new ClientData(clientData.accountNo, clientData.status, clientData.officeId, clientData.officeName, clientData.transferToOfficeId, clientData.transferToOfficeName, clientData.id, clientData.firstname, clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.mobileNo, - clientData.activationDate, clientData.imageId, clientData.staffId, clientData.staffName, clientData.officeOptions, - clientData.groups, clientData.staffOptions, null, null, clientData.savingProductOptions, clientData.savingsProductId, - clientData.savingsProductName, clientData.savingsAccountId, savingAccountOptions); + clientData.dateOfBirth, clientData.gender, clientData.activationDate, clientData.imageId, clientData.staffId, + clientData.staffName, clientData.officeOptions, clientData.groups, clientData.staffOptions, null, null, null, + clientData.savingProductOptions, clientData.savingsProductId, clientData.savingsProductName, clientData.savingsAccountId, + savingAccountOptions); } @@ -103,9 +110,10 @@ final public class ClientData implements Comparable { return new ClientData(clientData.accountNo, clientData.status, clientData.officeId, clientData.officeName, clientData.transferToOfficeId, clientData.transferToOfficeName, clientData.id, clientData.firstname, clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.mobileNo, - clientData.activationDate, clientData.imageId, clientData.staffId, clientData.staffName, clientData.officeOptions, - parentGroups, clientData.staffOptions, null, clientData.timeline, clientData.savingProductOptions, - clientData.savingsProductId, clientData.savingsProductName, clientData.savingsAccountId, clientData.savingAccountOptions); + clientData.dateOfBirth, clientData.gender, clientData.activationDate, clientData.imageId, clientData.staffId, + clientData.staffName, clientData.officeOptions, parentGroups, clientData.staffOptions, null, null, clientData.timeline, + clientData.savingProductOptions, clientData.savingsProductId, clientData.savingsProductName, clientData.savingsAccountId, + clientData.savingAccountOptions); } @@ -114,33 +122,35 @@ final public class ClientData implements Comparable { final String officeName) { return new ClientData(accountNo, status, officeId, officeName, null, null, id, firstname, middlename, lastname, fullname, - displayName, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); + displayName, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); } public static ClientData lookup(final Long id, final String displayName, final Long officeId, final String officeName) { return new ClientData(null, null, officeId, officeName, null, null, id, null, null, null, null, displayName, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null); + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); } public static ClientData instance(final String accountNo, final EnumOptionData status, final Long officeId, final String officeName, final Long transferToOfficeId, final String transferToOfficeName, final Long id, final String firstname, final String middlename, final String lastname, final String fullname, final String displayName, final String externalId, - final String mobileNo, final LocalDate activationDate, final Long imageId, final Long staffId, final String staffName, - final ClientTimelineData timeline, final Long savingsProductId, final String savingsProductName, final Long savingsAccountId) { + final String mobileNo, final LocalDate dateOfBirth, final CodeValueData gender, final LocalDate activationDate, + final Long imageId, final Long staffId, final String staffName, final ClientTimelineData timeline, final Long savingsProductId, + final String savingsProductName, final Long savingsAccountId) { return new ClientData(accountNo, status, officeId, officeName, transferToOfficeId, transferToOfficeName, id, firstname, middlename, - lastname, fullname, displayName, externalId, mobileNo, activationDate, imageId, staffId, staffName, null, null, null, null, - timeline, null, savingsProductId, savingsProductName, savingsAccountId, null); + lastname, fullname, displayName, externalId, mobileNo, dateOfBirth, gender, activationDate, imageId, staffId, staffName, + null, null, null, null, null, timeline, null, savingsProductId, savingsProductName, savingsAccountId, null); } private ClientData(final String accountNo, final EnumOptionData status, final Long officeId, final String officeName, final Long transferToOfficeId, final String transferToOfficeName, final Long id, final String firstname, final String middlename, final String lastname, final String fullname, final String displayName, final String externalId, - final String mobileNo, final LocalDate activationDate, final Long imageId, final Long staffId, final String staffName, - final Collection allowedOffices, final Collection groups, - final Collection staffOptions, final Collection closureReasons, final ClientTimelineData timeline, + final String mobileNo, final LocalDate dateOfBirth, final CodeValueData gender, final LocalDate activationDate, + final Long imageId, final Long staffId, final String staffName, final Collection allowedOffices, + final Collection groups, final Collection staffOptions, + final Collection closureReasons, final Collection genderOptions, final ClientTimelineData timeline, final Collection savingProductOptions, final Long savingsProductId, final String savingsProductName, final Long savingsAccountId, final Collection savingAccountOptions) { this.accountNo = accountNo; @@ -163,6 +173,8 @@ final public class ClientData implements Comparable { this.externalId = StringUtils.defaultIfEmpty(externalId, null); this.mobileNo = StringUtils.defaultIfEmpty(mobileNo, null); this.activationDate = activationDate; + this.dateOfBirth = dateOfBirth; + this.gender = gender; this.imageId = imageId; if (imageId != null) { this.imagePresent = Boolean.TRUE; @@ -179,6 +191,7 @@ final public class ClientData implements Comparable { this.officeOptions = allowedOffices; this.staffOptions = staffOptions; this.closureReasons = closureReasons; + this.genderOptions = genderOptions; this.timeline = timeline; this.savingProductOptions = savingProductOptions; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientDataValidator.java index 230967b7a..5ac2c2e0a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientDataValidator.java @@ -21,6 +21,7 @@ import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.portfolio.client.api.ClientApiConstants; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -144,6 +145,17 @@ public final class ClientDataValidator { baseDataValidator.reset().parameter(ClientApiConstants.submittedOnDateParamName).value(submittedOnDate).notNull(); } + if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.dateOfBirthParamName, element)) { + final LocalDate dateOfBirth = this.fromApiJsonHelper.extractLocalDateNamed(ClientApiConstants.dateOfBirthParamName, element); + baseDataValidator.reset().parameter(ClientApiConstants.dateOfBirthParamName).value(dateOfBirth).notNull() + .validateDateBefore(DateUtils.getLocalDateOfTenant()); + } + + if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.genderIdParamName, element)) { + final Integer genderId = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(ClientApiConstants.genderIdParamName, element); + baseDataValidator.reset().parameter(ClientApiConstants.genderIdParamName).value(genderId).notNull(); + } + throwExceptionIfValidationWarningsExist(dataValidationErrors); } @@ -318,6 +330,17 @@ public final class ClientDataValidator { .longGreaterThanZero(); } + if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.dateOfBirthParamName, element)) { + final LocalDate dateOfBirth = this.fromApiJsonHelper.extractLocalDateNamed(ClientApiConstants.dateOfBirthParamName, element); + baseDataValidator.reset().parameter(ClientApiConstants.dateOfBirthParamName).value(dateOfBirth).notNull() + .validateDateBefore(DateUtils.getLocalDateOfTenant()); + } + + if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.genderIdParamName, element)) { + final Integer genderId = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(ClientApiConstants.genderIdParamName, element); + baseDataValidator.reset().parameter(ClientApiConstants.genderIdParamName).value(genderId).notNull(); + } + if (!atLeastOneParameterPassedForUpdate) { final Object forceError = null; baseDataValidator.reset().anyOfNotNull(forceError); @@ -425,7 +448,7 @@ public final class ClientDataValidator { throwExceptionIfValidationWarningsExist(dataValidationErrors); } - + public void validateForSavingsAccount(final String json) { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } @@ -450,5 +473,4 @@ public final class ClientDataValidator { } - } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java index 2aa37ea13..845f78b6d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java @@ -105,6 +105,14 @@ public final class Client extends AbstractPersistable { @Column(name = "external_id", length = 100, nullable = true, unique = true) private String externalId; + @Column(name = "date_of_birth", nullable = true) + @Temporal(TemporalType.DATE) + private Date dateOfBirth; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "gender_cv_id", nullable = true) + private CodeValue gender; + @ManyToOne @JoinColumn(name = "staff_id") private Staff staff; @@ -144,13 +152,13 @@ public final class Client extends AbstractPersistable { @ManyToOne @JoinColumn(name = "default_savings_product", nullable = true) private SavingsProduct savingsProduct; - + @ManyToOne @JoinColumn(name = "default_savings_account", nullable = true) private SavingsAccount savingsAccount; public static Client createNew(final AppUser currentUser, final Office clientOffice, final Group clientParentGroup, final Staff staff, - final SavingsProduct savingsProduct, final JsonCommand command) { + final SavingsProduct savingsProduct, final CodeValue gender, final JsonCommand command) { final String accountNo = command.stringValueOfParameterNamed(ClientApiConstants.accountNoParamName); final String externalId = command.stringValueOfParameterNamed(ClientApiConstants.externalIdParamName); @@ -161,6 +169,8 @@ public final class Client extends AbstractPersistable { final String lastname = command.stringValueOfParameterNamed(ClientApiConstants.lastnameParamName); final String fullname = command.stringValueOfParameterNamed(ClientApiConstants.fullnameParamName); + final LocalDate dataOfBirth = command.localDateValueOfParameterNamed(ClientApiConstants.dateOfBirthParamName); + ClientStatus status = ClientStatus.PENDING; boolean active = false; if (command.hasParameter("active")) { @@ -182,9 +192,10 @@ public final class Client extends AbstractPersistable { if (command.hasParameter(ClientApiConstants.submittedOnDateParamName)) { submittedOnDate = command.localDateValueOfParameterNamed(ClientApiConstants.submittedOnDateParamName); } - + final SavingsAccount account = null; return new Client(currentUser, status, clientOffice, clientParentGroup, accountNo, firstname, middlename, lastname, fullname, - activationDate, officeJoiningDate, externalId, mobileNo, staff, submittedOnDate, savingsProduct, null); + activationDate, officeJoiningDate, externalId, mobileNo, staff, submittedOnDate, savingsProduct, account, dataOfBirth, + gender); } protected Client() { @@ -194,7 +205,8 @@ public final class Client extends AbstractPersistable { private Client(final AppUser currentUser, final ClientStatus status, final Office office, final Group clientParentGroup, final String accountNo, final String firstname, final String middlename, final String lastname, final String fullname, final LocalDate activationDate, final LocalDate officeJoiningDate, final String externalId, final String mobileNo, - final Staff staff, final LocalDate submittedOnDate, final SavingsProduct savingsProduct,final SavingsAccount savingsAccount) { + final Staff staff, final LocalDate submittedOnDate, final SavingsProduct savingsProduct, final SavingsAccount savingsAccount, + final LocalDate dateOfBirth, final CodeValue gender) { if (StringUtils.isBlank(accountNo)) { this.accountNumber = new RandomPasswordGenerator(19).generate(); @@ -260,6 +272,13 @@ public final class Client extends AbstractPersistable { this.savingsProduct = savingsProduct; this.savingsAccount = savingsAccount; + if(gender != null){ + this.gender = gender; + } + if (dateOfBirth != null) { + this.dateOfBirth = dateOfBirth.toDateTimeAtStartOfDay().toDate(); + } + deriveDisplayName(); validate(); } @@ -666,11 +685,11 @@ public final class Client extends AbstractPersistable { return this.activatedBy; } - public SavingsAccount savingsAccount() { - return this.savingsAccount; - } + public SavingsAccount savingsAccount() { + return this.savingsAccount; + } - public void updateSavingsAccount(SavingsAccount savingsAccount) { - this.savingsAccount = savingsAccount; - } + public void updateSavingsAccount(SavingsAccount savingsAccount) { + this.savingsAccount = savingsAccount; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java index fdee2c752..b22b672c0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java @@ -26,6 +26,7 @@ import org.mifosplatform.organisation.office.data.OfficeData; import org.mifosplatform.organisation.office.service.OfficeReadPlatformService; import org.mifosplatform.organisation.staff.data.StaffData; import org.mifosplatform.organisation.staff.service.StaffReadPlatformService; +import org.mifosplatform.portfolio.client.api.ClientApiConstants; import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.data.ClientTimelineData; import org.mifosplatform.portfolio.client.domain.ClientEnumerations; @@ -94,8 +95,10 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService if (CollectionUtils.isEmpty(staffOptions)) { staffOptions = null; } + final List genderOptions = new ArrayList( + this.codeValueReadPlatformService.retrieveCodeValuesByCode(ClientApiConstants.GENDER)); - return ClientData.template(defaultOfficeId, new LocalDate(), offices, staffOptions, null, savingsProductDatas); + return ClientData.template(defaultOfficeId, new LocalDate(), offices, staffOptions, null, genderOptions, savingsProductDatas); } @Override @@ -270,6 +273,9 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService sqlBuilder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, "); sqlBuilder.append("c.fullname as fullname, c.display_name as displayName, "); sqlBuilder.append("c.mobile_no as mobileNo, "); + sqlBuilder.append("c.date_of_birth as dateOfBirth, "); + sqlBuilder.append("c.gender_cv_id as genderId, "); + sqlBuilder.append("cv.code_value as genderValue, "); sqlBuilder.append("c.activation_date as activationDate, c.image_id as imageId, "); sqlBuilder.append("c.staff_id as staffId, s.display_name as staffName,"); sqlBuilder.append("c.default_savings_product as savingsProductId, sp.name as savingsProductName, "); @@ -299,6 +305,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService sqlBuilder.append("left join m_appuser sbu on sbu.id = c.submittedon_userid "); sqlBuilder.append("left join m_appuser acu on acu.id = c.activatedon_userid "); sqlBuilder.append("left join m_appuser clu on clu.id = c.closedon_userid "); + sqlBuilder.append("left join m_code_value cv on cv.id = c.gender_cv_id "); this.schema = sqlBuilder.toString(); } @@ -329,6 +336,10 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService final String displayName = rs.getString("displayName"); final String externalId = rs.getString("externalId"); final String mobileNo = rs.getString("mobileNo"); + final LocalDate dateOfBirth = JdbcSupport.getLocalDate(rs, "dateOfBirth"); + final Long genderId = JdbcSupport.getLong(rs, "genderId"); + final String genderValue = rs.getString("genderValue"); + final CodeValueData gender = CodeValueData.instance(genderId, genderValue); final LocalDate activationDate = JdbcSupport.getLocalDate(rs, "activationDate"); final Long imageId = JdbcSupport.getLong(rs, "imageId"); @@ -337,9 +348,8 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService final Long savingsProductId = JdbcSupport.getLong(rs, "savingsProductId"); final String savingsProductName = rs.getString("savingsProductName"); - + final Long savingsAccountId = JdbcSupport.getLong(rs, "savingsAccountId"); - final LocalDate closedOnDate = JdbcSupport.getLocalDate(rs, "closedOnDate"); final String closedByUsername = rs.getString("closedByUsername"); @@ -360,8 +370,8 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService closedByUsername, closedByFirstname, closedByLastname); return ClientData.instance(accountNo, status, officeId, officeName, transferToOfficeId, transferToOfficeName, id, firstname, - middlename, lastname, fullname, displayName, externalId, mobileNo, activationDate, imageId, staffId, staffName, - timeline, savingsProductId, savingsProductName,savingsAccountId); + middlename, lastname, fullname, displayName, externalId, mobileNo, dateOfBirth, gender, activationDate, imageId, + staffId, staffName, timeline, savingsProductId, savingsProductName, savingsAccountId); } } @@ -394,6 +404,9 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService builder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, "); builder.append("c.fullname as fullname, c.display_name as displayName, "); builder.append("c.mobile_no as mobileNo, "); + builder.append("c.date_of_birth as dateOfBirth, "); + builder.append("c.gender_cv_id as genderId, "); + builder.append("cv.code_value as genderValue, "); builder.append("c.submittedon_date as submittedOnDate, "); builder.append("sbu.username as submittedByUsername, "); @@ -422,6 +435,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService builder.append("left join m_appuser sbu on sbu.id = c.submittedon_userid "); builder.append("left join m_appuser acu on acu.id = c.activatedon_userid "); builder.append("left join m_appuser clu on clu.id = c.closedon_userid "); + builder.append("left join m_code_value cv on cv.id = c.gender_cv_id "); this.schema = builder.toString(); } @@ -452,6 +466,10 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService final String displayName = rs.getString("displayName"); final String externalId = rs.getString("externalId"); final String mobileNo = rs.getString("mobileNo"); + final LocalDate dateOfBirth = JdbcSupport.getLocalDate(rs, "dateOfBirth"); + final Long genderId = JdbcSupport.getLong(rs, "genderId"); + final String genderValue = rs.getString("genderValue"); + final CodeValueData gender = CodeValueData.instance(genderId, genderValue); final LocalDate activationDate = JdbcSupport.getLocalDate(rs, "activationDate"); final Long imageId = JdbcSupport.getLong(rs, "imageId"); final Long staffId = JdbcSupport.getLong(rs, "staffId"); @@ -460,7 +478,6 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService final Long savingsProductId = JdbcSupport.getLong(rs, "savingsProductId"); final String savingsProductName = rs.getString("savingsProductName"); final Long savingsAccountId = JdbcSupport.getLong(rs, "savingsAccountId"); - final LocalDate closedOnDate = JdbcSupport.getLocalDate(rs, "closedOnDate"); final String closedByUsername = rs.getString("closedByUsername"); @@ -481,8 +498,8 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService closedByUsername, closedByFirstname, closedByLastname); return ClientData.instance(accountNo, status, officeId, officeName, transferToOfficeId, transferToOfficeName, id, firstname, - middlename, lastname, fullname, displayName, externalId, mobileNo, activationDate, imageId, staffId, staffName, - timeline, savingsProductId, savingsProductName,savingsAccountId); + middlename, lastname, fullname, displayName, externalId, mobileNo, dateOfBirth, gender, activationDate, imageId, + staffId, staffName, timeline, savingsProductId, savingsProductName, savingsAccountId); } } @@ -594,7 +611,8 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService public ClientData retrieveAllClosureReasons(final String clientClosureReason) { final List closureReasons = new ArrayList( this.codeValueReadPlatformService.retrieveCodeValuesByCode(clientClosureReason)); - return ClientData.template(null, null, null, null, closureReasons, null); + + return ClientData.template(null, null, null, null, closureReasons, null, null); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java index 9c8bcef74..c1ea91100 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java @@ -183,6 +183,13 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP staff = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(staffId, clientOffice.getHierarchy()); } + CodeValue gender = null; + final Long genderId = command.longValueOfParameterNamed(ClientApiConstants.genderIdParamName); + if (genderId != null) { + gender = this.codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.GENDER, + genderId); + } + SavingsProduct savingsProduct = null; final Long savingsProductId = command.longValueOfParameterNamed(ClientApiConstants.savingsProductIdParamName); if (savingsProductId != null) { @@ -191,7 +198,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP } - final Client newClient = Client.createNew(currentUser, clientOffice, clientParentGroup, staff, savingsProduct, command); + final Client newClient = Client.createNew(currentUser, clientOffice, clientParentGroup, staff, savingsProduct, gender, command); boolean rollbackTransaction = false; if (newClient.isActive()) { final CommandWrapper commandWrapper = new CommandWrapperBuilder().activateClient(null).build(); @@ -496,7 +503,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP if (savingsId != null) { savingsAccount = this.savingsRepository.findOne(savingsId); if (savingsAccount == null) { throw new SavingsAccountNotFoundException(savingsId); } - if(!savingsAccount.getClient().identifiedBy(clientId)){ + if (!savingsAccount.getClient().identifiedBy(clientId)) { String defaultUserMessage = "saving account must belongs to client"; throw new InvalidClientSavingProductException("saving.account", "must.belongs.to.client", defaultUserMessage, savingsId, clientForUpdate.getId()); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/InterestRateChartApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/InterestRateChartApiConstants.java new file mode 100644 index 000000000..81b65b197 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/InterestRateChartApiConstants.java @@ -0,0 +1,59 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class InterestRateChartApiConstants { + + public static final String INTERESTRATE_CHART_RESOURCE_NAME = "interestchart"; + + // actions + public static String summitalAction = ".summital"; + + // command + public static String COMMAND_UNDO_TRANSACTION = "undo"; + + // general + public static final String localeParamName = "locale"; + public static final String dateFormatParamName = "dateFormat"; + + // interest rate chart parameters + public static final String idParamName = "id"; + public static final String nameParamName = "name"; + public static final String descriptionParamName = "description"; + public static final String fromDateParamName = "fromDate"; + public static final String endDateParamName = "endDate"; + public static final String savingsProductIdParamName = "savingsProductId"; + public static final String savingsProductNameParamName = "savingsProductName"; + + // interest rate chart Slabs parameters + public static final String periodTypeParamName = "periodType"; + public static final String fromPeriodParamName = "fromPeriod"; + public static final String toPeriodParamName = "toPeriod"; + public static final String amountRangeFromParamName = "amountRangeFrom"; + public static final String amountRangeToParamName = "amountRangeTo"; + public static final String annualInterestRateParamName = "annualInterestRate"; + + // associations + public static final String chartSlabs = "chartSlabs"; + + // to delete chart Slabs from chart + public static final String deleteParamName = "delete"; + + public static final Set INTERESTRATE_CHART_CREATE_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, + dateFormatParamName, nameParamName, descriptionParamName, fromDateParamName, endDateParamName, savingsProductIdParamName, + chartSlabs)); + + public static final Set INTERESTRATE_CHART_UPDATE_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, + dateFormatParamName, idParamName, nameParamName, descriptionParamName, fromDateParamName, endDateParamName, chartSlabs, + deleteParamName)); + + public static final Set INTERESTRATE_CHART_RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, + dateFormatParamName, idParamName, nameParamName, descriptionParamName, fromDateParamName, endDateParamName, chartSlabs)); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/InterestRateChartPeriodType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/InterestRateChartPeriodType.java new file mode 100644 index 000000000..79386c4d7 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/InterestRateChartPeriodType.java @@ -0,0 +1,74 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart; + +import java.util.ArrayList; +import java.util.List; + + +/** + * An enumeration of supported calendar periods used in savings. + */ +public enum InterestRateChartPeriodType { + + DAYS(0, "interestChartPeriodType.days"), // + WEEKS(1, "interestChartPeriodType.weeks"), // + MONTHS(2, "interestChartPeriodType.months"), // + YEARS(3, "interestChartPeriodType.years"), // + INVALID(4, "interestChartPeriodType.invalid"); + + private final Integer value; + private final String code; + + private InterestRateChartPeriodType(final Integer value, final String code) { + this.value = value; + this.code = code; + } + + public Integer getValue() { + return this.value; + } + + public String getCode() { + return this.code; + } + + public static InterestRateChartPeriodType fromInt(final Integer type) { + InterestRateChartPeriodType periodType = InterestRateChartPeriodType.INVALID; + if (type != null) { + switch (type) { + case 0: + periodType = InterestRateChartPeriodType.DAYS; + break; + case 1: + periodType = InterestRateChartPeriodType.WEEKS; + break; + case 2: + periodType = InterestRateChartPeriodType.MONTHS; + break; + case 3: + periodType = InterestRateChartPeriodType.YEARS; + break; + } + } + return periodType; + } + + public static Object[] integerValues() { + final List values = new ArrayList(); + for (final InterestRateChartPeriodType enumType : values()) { + if (!enumType.isInvalid()) { + values.add(enumType.getValue()); + } + } + + return values.toArray(); + } + + public boolean isInvalid(){ + return this.value.equals(InterestRateChartPeriodType.INVALID.value); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/InterestRateChartSlabApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/InterestRateChartSlabApiConstants.java new file mode 100644 index 000000000..794b0b9ce --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/InterestRateChartSlabApiConstants.java @@ -0,0 +1,48 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class InterestRateChartSlabApiConstants { + + public static final String INTERESTRATE_CHART_SLAB_RESOURCE_NAME = "chartslab"; + + // actions + + // command + + // general + public static final String localeParamName = "locale"; + public static final String dateFormatParamName = "dateFormat"; + + // interest rate chart Slabs parameters + public static final String idParamName = "id"; + public static final String descriptionParamName = "description"; + public static final String periodTypeParamName = "periodType"; + public static final String fromPeriodParamName = "fromPeriod"; + public static final String toPeriodParamName = "toPeriod"; + public static final String amountRangeFromParamName = "amountRangeFrom"; + public static final String amountRangeToParamName = "amountRangeTo"; + public static final String annualInterestRateParamName = "annualInterestRate"; + public static final String currencyCodeParamName = "currencyCode"; + + // associations + + public static final Set INTERESTRATE_CHART_SLAB_CREATE_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList( + localeParamName, idParamName, descriptionParamName, periodTypeParamName, fromPeriodParamName, toPeriodParamName, + amountRangeFromParamName, amountRangeToParamName, annualInterestRateParamName, currencyCodeParamName)); + + public static final Set INTERESTRATE_CHART_SLAB_UPDATE_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList( + localeParamName, idParamName, descriptionParamName, periodTypeParamName, fromPeriodParamName, toPeriodParamName, + amountRangeFromParamName, amountRangeToParamName, annualInterestRateParamName, currencyCodeParamName)); + + public static final Set INTERESTRATE_CHART_SLAB_RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, + idParamName, descriptionParamName, periodTypeParamName, fromPeriodParamName, toPeriodParamName, amountRangeFromParamName, + amountRangeToParamName, annualInterestRateParamName, currencyCodeParamName)); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/api/InterestRateChartSlabsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/api/InterestRateChartSlabsApiResource.java new file mode 100644 index 000000000..352278774 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/api/InterestRateChartSlabsApiResource.java @@ -0,0 +1,139 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.api; + +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartSlabApiConstants.INTERESTRATE_CHART_SLAB_RESOURCE_NAME; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartSlabApiConstants.INTERESTRATE_CHART_SLAB_RESPONSE_DATA_PARAMETERS; + +import java.util.Collection; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; + +import org.mifosplatform.commands.domain.CommandWrapper; +import org.mifosplatform.commands.service.CommandWrapperBuilder; +import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; +import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartSlabData; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartSlabReadPlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Path("/interestratecharts/{chartId}/chartslabs") +@Component +@Scope("singleton") +public class InterestRateChartSlabsApiResource { + + private final InterestRateChartSlabReadPlatformService interestRateChartSlabReadPlatformService; + private final PlatformSecurityContext context; + private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final ApiRequestParameterHelper apiRequestParameterHelper; + + @Autowired + public InterestRateChartSlabsApiResource(final InterestRateChartSlabReadPlatformService interestRateChartSlabReadPlatformService, + final PlatformSecurityContext context, final DefaultToApiJsonSerializer toApiJsonSerializer, + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, + final ApiRequestParameterHelper apiRequestParameterHelper) { + + this.context = context; + this.toApiJsonSerializer = toApiJsonSerializer; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + this.apiRequestParameterHelper = apiRequestParameterHelper; + this.interestRateChartSlabReadPlatformService = interestRateChartSlabReadPlatformService; + } + + @GET + @Path("template") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String template() { + // TODO: implement + return null; + } + + @GET + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveAll(@PathParam("chartId") final Long chartId, @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(INTERESTRATE_CHART_SLAB_RESOURCE_NAME); + Collection chartSlabs = this.interestRateChartSlabReadPlatformService.retrieveAll(chartId); + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + return this.toApiJsonSerializer.serialize(settings, chartSlabs, INTERESTRATE_CHART_SLAB_RESPONSE_DATA_PARAMETERS); + } + + @GET + @Path("{chartSlabId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveOne(@PathParam("chartId") final Long chartId, @PathParam("chartSlabId") final Long chartSlabId, + @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(INTERESTRATE_CHART_SLAB_RESOURCE_NAME); + + InterestRateChartSlabData chartSlab = this.interestRateChartSlabReadPlatformService.retrieveOne(chartId, chartSlabId); + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + if (settings.isTemplate()) { + chartSlab = this.interestRateChartSlabReadPlatformService.retrieveWithTemplate(chartSlab); + } + + return this.toApiJsonSerializer.serialize(settings, chartSlab, INTERESTRATE_CHART_SLAB_RESPONSE_DATA_PARAMETERS); + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String create(@PathParam("chartId") final Long chartId, final String apiRequestBodyAsJson) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().createInterestRateChartSlab(chartId) + .withJson(apiRequestBodyAsJson).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + @PUT + @Path("{chartSlabId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String update(@PathParam("chartId") final Long chartId, @PathParam("chartSlabId") final Long chartSlabId, + final String apiRequestBodyAsJson) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().updateInterestRateChartSlab(chartId, chartSlabId) + .withJson(apiRequestBodyAsJson).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + @DELETE + @Path("{chartSlabId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String delete(@PathParam("chartId") final Long chartId, @PathParam("chartSlabId") final Long chartSlabId) { + final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteInterestRateChartSlab(chartId, chartSlabId).build(); + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + return this.toApiJsonSerializer.serialize(result); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/api/InterestRateChartsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/api/InterestRateChartsApiResource.java new file mode 100644 index 000000000..dfd078b15 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/api/InterestRateChartsApiResource.java @@ -0,0 +1,143 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.api; + +import java.util.Collection; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; + +import org.mifosplatform.commands.domain.CommandWrapper; +import org.mifosplatform.commands.service.CommandWrapperBuilder; +import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; +import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants; +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartData; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartReadPlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Path("/interestratecharts") +@Component +@Scope("singleton") +public class InterestRateChartsApiResource { + + private final InterestRateChartReadPlatformService chartReadPlatformService; + private final PlatformSecurityContext context; + private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final ApiRequestParameterHelper apiRequestParameterHelper; + + @Autowired + public InterestRateChartsApiResource(final InterestRateChartReadPlatformService chartReadPlatformService, + final PlatformSecurityContext context, final DefaultToApiJsonSerializer toApiJsonSerializer, + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, + final ApiRequestParameterHelper apiRequestParameterHelper) { + + this.context = context; + this.toApiJsonSerializer = toApiJsonSerializer; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + this.apiRequestParameterHelper = apiRequestParameterHelper; + this.chartReadPlatformService = chartReadPlatformService; + } + + @GET + @Path("template") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String template(@Context final UriInfo uriInfo) { + this.context.authenticatedUser().validateHasReadPermission(InterestRateChartApiConstants.INTERESTRATE_CHART_RESOURCE_NAME); + + InterestRateChartData chartData = this.chartReadPlatformService.template(); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, chartData, + InterestRateChartApiConstants.INTERESTRATE_CHART_RESPONSE_DATA_PARAMETERS); + } + + @GET + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveAll(@Context final UriInfo uriInfo, @QueryParam("productId") final Long productId) { + + this.context.authenticatedUser().validateHasReadPermission(InterestRateChartApiConstants.INTERESTRATE_CHART_RESOURCE_NAME); + + Collection chartDatas = this.chartReadPlatformService.retrieveAllWithSlabs(productId); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, chartDatas, + InterestRateChartApiConstants.INTERESTRATE_CHART_RESPONSE_DATA_PARAMETERS); + } + + @GET + @Path("{chartId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveOne(@PathParam("chartId") final Long chartId, @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(InterestRateChartApiConstants.INTERESTRATE_CHART_RESOURCE_NAME); + + InterestRateChartData chartData = this.chartReadPlatformService.retrieveOneWithSlabs(chartId); + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + if (settings.isTemplate()) { + chartData = this.chartReadPlatformService.retrieveWithTemplate(chartData); + } + + return this.toApiJsonSerializer.serialize(settings, chartData, + InterestRateChartApiConstants.INTERESTRATE_CHART_RESPONSE_DATA_PARAMETERS); + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String create(final String apiRequestBodyAsJson) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().createInterestRateChart().withJson(apiRequestBodyAsJson).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + @PUT + @Path("{chartId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String update(@PathParam("chartId") final Long chartId, final String apiRequestBodyAsJson) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().updateInterestRateChart(chartId) + .withJson(apiRequestBodyAsJson).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + @DELETE + @Path("{chartId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String delete(@PathParam("chartId") final Long chartId) { + final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteInterestRateChart(chartId).build(); + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + return this.toApiJsonSerializer.serialize(result); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartData.java new file mode 100644 index 000000000..f65f39bb4 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartData.java @@ -0,0 +1,116 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.data; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; + +/** + * Immutable data object representing a InterestRateChart. + */ +public class InterestRateChartData { + + private final Long id; + private final String name; + private final String description; + private final LocalDate fromDate; + private final LocalDate endDate; + private final Long savingsProductId; + private final String savingsProductName; + + // associations + private Set chartSlabs; + + // template + private Collection periodTypes; + + public static InterestRateChartData instance(Long id, String name, String description, LocalDate fromDate, LocalDate endDate, + Long savingsProductId, String savingsProductName, Set chartSlabs) { + Collection periodTypes = null; + return new InterestRateChartData(id, name, description, fromDate, endDate, savingsProductId, savingsProductName, chartSlabs, + periodTypes); + } + + public static InterestRateChartData withSlabs(InterestRateChartData interestRateChartData, + Set chartSlabs) { + return new InterestRateChartData(interestRateChartData.id, interestRateChartData.name, interestRateChartData.description, + interestRateChartData.fromDate, interestRateChartData.endDate, interestRateChartData.savingsProductId, + interestRateChartData.savingsProductName, chartSlabs, interestRateChartData.periodTypes); + } + + public static InterestRateChartData withTemplate(InterestRateChartData interestRateChartData, Collection periodTypes) { + return new InterestRateChartData(interestRateChartData.id, interestRateChartData.name, interestRateChartData.description, + interestRateChartData.fromDate, interestRateChartData.endDate, interestRateChartData.savingsProductId, + interestRateChartData.savingsProductName, interestRateChartData.chartSlabs, periodTypes); + } + + public static InterestRateChartData template(Collection periodTypes) { + final Long id = null; + final String name = null; + final String description = null; + final LocalDate fromDate = null; + final LocalDate endDate = null; + final Long savingsProductId = null; + final String savingsProductName = null; + final Set chartSlabs = null; + + return new InterestRateChartData(id, name, description, fromDate, endDate, savingsProductId, savingsProductName, chartSlabs, + periodTypes); + } + + private InterestRateChartData(Long id, String name, String description, LocalDate fromDate, LocalDate endDate, Long savingsProductId, + String savingsProductName, Set chartSlabs, Collection periodTypes) { + this.id = id; + this.name = name; + this.description = description; + this.fromDate = fromDate; + this.endDate = endDate; + this.chartSlabs = chartSlabs; + this.savingsProductId = savingsProductId; + this.savingsProductName = savingsProductName; + this.periodTypes = periodTypes; + } + + public void addChartSlab(final InterestRateChartSlabData chartSlab) { + if (this.chartSlabs == null) { + this.chartSlabs = new HashSet(); + } + + this.chartSlabs.add(chartSlab); + } + + public boolean isFromDateAfter(final LocalDate compareDate) { + return (compareDate == null) ? false : this.fromDate.isAfter(compareDate); + } + + public LocalDate endDate() { + return this.endDate; + } + + public LocalDate fromDate() { + return this.fromDate; + } + + public String name() { + return this.name; + } + + public String description() { + return this.description; + } + + public Set chartSlabs() { + return this.chartSlabs; + } + + public Collection periodTypes() { + return this.periodTypes; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartDataValidator.java new file mode 100644 index 000000000..2cd04620a --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartDataValidator.java @@ -0,0 +1,176 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.data; + +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.INTERESTRATE_CHART_CREATE_REQUEST_DATA_PARAMETERS; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.INTERESTRATE_CHART_RESOURCE_NAME; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.INTERESTRATE_CHART_UPDATE_REQUEST_DATA_PARAMETERS; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.chartSlabs; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.descriptionParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.endDateParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.fromDateParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.idParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.nameParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.savingsProductIdParamName; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; + +@Component +public class InterestRateChartDataValidator { + + private final FromJsonHelper fromApiJsonHelper; + private final InterestRateChartSlabDataValidator chartSlabDataValidator; + + @Autowired + public InterestRateChartDataValidator(final FromJsonHelper fromApiJsonHelper, + final InterestRateChartSlabDataValidator chartSlabDataValidator) { + this.fromApiJsonHelper = fromApiJsonHelper; + this.chartSlabDataValidator = chartSlabDataValidator; + } + + public void validateForCreate(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(INTERESTRATE_CHART_RESOURCE_NAME); + + validateForCreate(json, baseDataValidator); + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public void validateForCreate(final String json, final DataValidatorBuilder baseDataValidator) { + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, INTERESTRATE_CHART_CREATE_REQUEST_DATA_PARAMETERS); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + + if (this.fromApiJsonHelper.parameterExists(nameParamName, element)) { + final String name = this.fromApiJsonHelper.extractStringNamed(nameParamName, element); + baseDataValidator.reset().parameter(nameParamName).value(name).notBlank(); + } + + if (this.fromApiJsonHelper.parameterExists(descriptionParamName, element)) { + final String description = this.fromApiJsonHelper.extractStringNamed(descriptionParamName, element); + baseDataValidator.reset().parameter(descriptionParamName).value(description).notNull(); + } + + final LocalDate fromDate = this.fromApiJsonHelper.extractLocalDateNamed(fromDateParamName, element); + baseDataValidator.reset().parameter(fromDateParamName).value(fromDate).notNull(); + + LocalDate toDate = null; + if (this.fromApiJsonHelper.parameterExists(endDateParamName, element)) { + toDate = this.fromApiJsonHelper.extractLocalDateNamed(endDateParamName, element); + baseDataValidator.reset().parameter(endDateParamName).value(toDate).notNull(); + } + + if (fromDate != null && toDate != null) { + if (fromDate.isAfter(toDate)) { + baseDataValidator.parameter(fromDateParamName).value(fromDate).failWithCode("from.date.is.after.to.date"); + } + } + + // validate chart Slabs + validateChartSlabs(element, baseDataValidator); + } + + public void validateUpdate(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(INTERESTRATE_CHART_RESOURCE_NAME); + this.validateForUpdate(json, baseDataValidator); + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public void validateForUpdate(final String json, final DataValidatorBuilder baseDataValidator) { + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, INTERESTRATE_CHART_UPDATE_REQUEST_DATA_PARAMETERS); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + + if (this.fromApiJsonHelper.parameterExists(savingsProductIdParamName, element)) { + final Long savingsProductId = this.fromApiJsonHelper.extractLongNamed(savingsProductIdParamName, element); + baseDataValidator.reset().parameter(savingsProductIdParamName).value(savingsProductId).notNull().integerGreaterThanZero(); + } + + if (this.fromApiJsonHelper.parameterExists(nameParamName, element)) { + final String name = this.fromApiJsonHelper.extractStringNamed(nameParamName, element); + baseDataValidator.reset().parameter(nameParamName).value(name).notBlank(); + } + + if (this.fromApiJsonHelper.parameterExists(descriptionParamName, element)) { + final String description = this.fromApiJsonHelper.extractStringNamed(descriptionParamName, element); + baseDataValidator.reset().parameter(descriptionParamName).value(description).notNull(); + } + + LocalDate fromDate = null; + if (this.fromApiJsonHelper.parameterExists(fromDateParamName, element)) { + fromDate = this.fromApiJsonHelper.extractLocalDateNamed(fromDateParamName, element); + baseDataValidator.reset().parameter(fromDateParamName).value(fromDate).notNull(); + } + + LocalDate toDate = null; + if (this.fromApiJsonHelper.parameterExists(endDateParamName, element)) { + toDate = this.fromApiJsonHelper.extractLocalDateNamed(endDateParamName, element); + baseDataValidator.reset().parameter(endDateParamName).value(toDate).notNull(); + } + + if (fromDate != null && toDate != null) { + if (fromDate.isAfter(toDate)) { + baseDataValidator.parameter(fromDateParamName).value(fromDate).failWithCode("from.date.is.after.to.date"); + } + } + + validateChartSlabs(element, baseDataValidator); + + } + + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + private void validateChartSlabs(JsonElement element, DataValidatorBuilder baseDataValidator) { + + if (element.isJsonObject()) { + final JsonObject topLevelJsonElement = element.getAsJsonObject(); + final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement); + if (topLevelJsonElement.has(chartSlabs) && topLevelJsonElement.get(chartSlabs).isJsonArray()) { + final JsonArray array = topLevelJsonElement.get(chartSlabs).getAsJsonArray(); + for (int i = 0; i < array.size(); i++) { + final JsonObject interstRateChartElement = array.get(i).getAsJsonObject(); + if (this.fromApiJsonHelper.parameterExists(idParamName, interstRateChartElement)) { + final Long id = this.fromApiJsonHelper.extractLongNamed(idParamName, interstRateChartElement); + baseDataValidator.reset().parameter(idParamName).value(id).notNull().integerGreaterThanZero(); + this.chartSlabDataValidator.validateChartSlabsUpdate(interstRateChartElement, baseDataValidator, locale); + } else { + this.chartSlabDataValidator.validateChartSlabsCreate(interstRateChartElement, baseDataValidator, locale); + } + } + } + } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartRepository.java new file mode 100644 index 000000000..242511bf6 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartRepository.java @@ -0,0 +1,14 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.data; + +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChart; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface InterestRateChartRepository extends JpaRepository, JpaSpecificationExecutor { + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartRepositoryWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartRepositoryWrapper.java new file mode 100644 index 000000000..7116f3be8 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartRepositoryWrapper.java @@ -0,0 +1,53 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.data; + +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChart; +import org.mifosplatform.portfolio.interestratechart.exception.InterestRateChartNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + *

+ * Wrapper for {@link InterestRateChartRepository} that is responsible for + * checking if {@link InterestRateChart} is returned when using + * findOne repository method and throwing an appropriate not found + * exception. + *

+ * + *

+ * This is to avoid need for checking and throwing in multiple areas of code + * base where {@link InterestRateChartRepository} is required. + *

+ */ +@Service +public class InterestRateChartRepositoryWrapper { + + private final InterestRateChartRepository repository; + + @Autowired + public InterestRateChartRepositoryWrapper(final InterestRateChartRepository repository) { + this.repository = repository; + } + + public InterestRateChart findOneWithNotFoundDetection(final Long intrestRateChartId) { + final InterestRateChart interestRateChart = this.repository.findOne(intrestRateChartId); + if (interestRateChart == null) { throw new InterestRateChartNotFoundException(intrestRateChartId); } + return interestRateChart; + } + + public void save(final InterestRateChart interestRateChart) { + this.repository.save(interestRateChart); + } + + public void delete(final InterestRateChart interestRateChart) { + this.repository.delete(interestRateChart); + } + + public void saveAndFlush(final InterestRateChart interestRateChart) { + this.repository.saveAndFlush(interestRateChart); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartSlabData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartSlabData.java new file mode 100644 index 000000000..2cea3a8ba --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartSlabData.java @@ -0,0 +1,101 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.data; + +import java.math.BigDecimal; +import java.util.Collection; + +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.organisation.monetary.data.CurrencyData; + +/** + * Immutable data object representing a InterestRateChartSlab. + */ +public class InterestRateChartSlabData { + + private final Long id; + private final String description; + private final EnumOptionData periodType; + private final Integer fromPeriod; + private final Integer toPeriod; + private final BigDecimal amountRangeFrom; + private final BigDecimal amountRangeTo; + private final BigDecimal annualInterestRate; + private final CurrencyData currency; + + // template + private Collection periodTypes; + + public static InterestRateChartSlabData instance(Long id, String description, EnumOptionData periodType, Integer fromPeriod, + Integer toPeriod, BigDecimal amountRangeFrom, BigDecimal amountRangeTo, BigDecimal annualInterestRate, CurrencyData currency) { + Collection periodTypes = null; + return new InterestRateChartSlabData(id, description, periodType, fromPeriod, toPeriod, amountRangeFrom, amountRangeTo, + annualInterestRate, currency, periodTypes); + } + + public static InterestRateChartSlabData withTemplate(InterestRateChartSlabData chartSlab, Collection periodTypes) { + return new InterestRateChartSlabData(chartSlab.id, chartSlab.description, chartSlab.periodType, chartSlab.fromPeriod, chartSlab.toPeriod, chartSlab.amountRangeFrom, chartSlab.amountRangeTo, + chartSlab.annualInterestRate, chartSlab.currency, periodTypes); + } + + private InterestRateChartSlabData(Long id, String description, EnumOptionData periodType, Integer fromPeriod, Integer toPeriod, + BigDecimal amountRangeFrom, BigDecimal amountRangeTo, BigDecimal annualInterestRate, CurrencyData currency, Collection periodTypes) { + this.id = id; + this.description = description; + this.periodType = periodType; + this.fromPeriod = fromPeriod; + this.toPeriod = toPeriod; + this.amountRangeFrom = amountRangeFrom; + this.amountRangeTo = amountRangeTo; + this.annualInterestRate = annualInterestRate; + this.currency = currency; + this.periodTypes = periodTypes; + } + + public String description() { + return this.description; + } + + + public EnumOptionData periodType() { + return this.periodType; + } + + + public Integer fromPeriod() { + return this.fromPeriod; + } + + + public Integer toPeriod() { + return this.toPeriod; + } + + + public BigDecimal amountRangeFrom() { + return this.amountRangeFrom; + } + + + public BigDecimal amountRangeTo() { + return this.amountRangeTo; + } + + + public BigDecimal annualInterestRate() { + return this.annualInterestRate; + } + + + public CurrencyData currency() { + return this.currency; + } + + public Collection periodTypes() { + return this.periodTypes; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartSlabDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartSlabDataValidator.java new file mode 100644 index 000000000..872a81384 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartSlabDataValidator.java @@ -0,0 +1,207 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.data; + +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartSlabApiConstants.INTERESTRATE_CHART_SLAB_CREATE_REQUEST_DATA_PARAMETERS; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartSlabApiConstants.INTERESTRATE_CHART_SLAB_RESOURCE_NAME; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartSlabApiConstants.INTERESTRATE_CHART_SLAB_UPDATE_REQUEST_DATA_PARAMETERS; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartSlabApiConstants.amountRangeFromParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartSlabApiConstants.amountRangeToParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartSlabApiConstants.annualInterestRateParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartSlabApiConstants.currencyCodeParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartSlabApiConstants.descriptionParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartSlabApiConstants.fromPeriodParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartSlabApiConstants.periodTypeParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartSlabApiConstants.toPeriodParamName; + +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.lang.StringUtils; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.portfolio.interestratechart.InterestRateChartPeriodType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; + +@Component +public class InterestRateChartSlabDataValidator { + + private final FromJsonHelper fromApiJsonHelper; + + @Autowired + public InterestRateChartSlabDataValidator(final FromJsonHelper fromApiJsonHelper) { + this.fromApiJsonHelper = fromApiJsonHelper; + } + + public void validateCreate(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, INTERESTRATE_CHART_SLAB_CREATE_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(INTERESTRATE_CHART_SLAB_RESOURCE_NAME); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + final JsonObject objectElement = element.getAsJsonObject(); + final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(objectElement); + + final String currencyCode = this.fromApiJsonHelper.extractStringNamed(currencyCodeParamName, element); + baseDataValidator.reset().parameter(currencyCodeParamName).value(currencyCode).notBlank().notExceedingLengthOf(3); + + validateChartSlabsCreate(element, baseDataValidator, locale); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public void validateChartSlabsCreate(final JsonElement element, final DataValidatorBuilder baseDataValidator, final Locale locale) { + + + if (this.fromApiJsonHelper.parameterExists(descriptionParamName, element)) { + final String description = this.fromApiJsonHelper.extractStringNamed(descriptionParamName, element); + baseDataValidator.reset().parameter(descriptionParamName).value(description).notNull(); + } + + final Integer periodType = this.fromApiJsonHelper.extractIntegerNamed(periodTypeParamName, element, locale); + baseDataValidator.reset().parameter(periodTypeParamName).value(periodType).notNull() + .isOneOfTheseValues(InterestRateChartPeriodType.integerValues()); + + Integer toPeriod = null; + + final Integer fromPeriod = this.fromApiJsonHelper.extractIntegerNamed(fromPeriodParamName, element, locale); + baseDataValidator.reset().parameter(fromPeriodParamName).value(fromPeriod).notNull().integerZeroOrGreater(); + + if (this.fromApiJsonHelper.parameterExists(toPeriodParamName, element)) { + toPeriod = this.fromApiJsonHelper.extractIntegerNamed(toPeriodParamName, element, locale); + baseDataValidator.reset().parameter(toPeriodParamName).value(toPeriod).notNull().integerZeroOrGreater(); + } + + if (fromPeriod != null && toPeriod != null) { + if (fromPeriod > toPeriod) { + baseDataValidator.parameter(fromPeriodParamName).value(fromPeriod).failWithCode("fromperiod.greater.than.to.period"); + } + } + BigDecimal amountRangeFrom = null; + BigDecimal amountRangeTo = null; + if (this.fromApiJsonHelper.parameterExists(amountRangeFromParamName, element)) { + amountRangeFrom = this.fromApiJsonHelper.extractBigDecimalNamed(amountRangeFromParamName, element, locale); + baseDataValidator.reset().parameter(amountRangeFromParamName).value(amountRangeFrom).notNull().positiveAmount(); + } + + if (this.fromApiJsonHelper.parameterExists(amountRangeToParamName, element)) { + amountRangeTo = this.fromApiJsonHelper.extractBigDecimalNamed(amountRangeToParamName, element, locale); + baseDataValidator.reset().parameter(amountRangeToParamName).value(amountRangeTo).notNull().positiveAmount(); + } + + if (amountRangeFrom != null && amountRangeTo != null) { + if (amountRangeFrom.compareTo(amountRangeTo) > 1) { + baseDataValidator.parameter(fromPeriodParamName).value(fromPeriod).failWithCode("fromperiod.greater.than.toperiod"); + } + } + + final BigDecimal annualInterestRate = this.fromApiJsonHelper.extractBigDecimalNamed(annualInterestRateParamName, element, locale); + baseDataValidator.reset().parameter(annualInterestRateParamName).value(annualInterestRate).notNull().zeroOrPositiveAmount(); + } + + public void validateUpdate(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, INTERESTRATE_CHART_SLAB_UPDATE_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(INTERESTRATE_CHART_SLAB_RESOURCE_NAME); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + final JsonObject objectElement = element.getAsJsonObject(); + final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(objectElement); + validateChartSlabsUpdate(element, baseDataValidator, locale); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public void validateChartSlabsUpdate(final JsonElement element, final DataValidatorBuilder baseDataValidator, final Locale locale) { + + + if (this.fromApiJsonHelper.parameterExists(descriptionParamName, element)) { + final String description = this.fromApiJsonHelper.extractStringNamed(descriptionParamName, element); + baseDataValidator.reset().parameter(descriptionParamName).value(description).notNull(); + } + + if (this.fromApiJsonHelper.parameterExists(periodTypeParamName, element)) { + final Integer periodType = this.fromApiJsonHelper.extractIntegerNamed(periodTypeParamName, element, locale); + baseDataValidator.reset().parameter(periodTypeParamName).value(periodType).notNull() + .isOneOfTheseValues(InterestRateChartPeriodType.integerValues()); + } + + Integer fromPeriod = null; + Integer toPeriod = null; + + if (this.fromApiJsonHelper.parameterExists(fromPeriodParamName, element)) { + fromPeriod = this.fromApiJsonHelper.extractIntegerNamed(fromPeriodParamName, element, locale); + baseDataValidator.reset().parameter(fromPeriodParamName).value(fromPeriod).notNull().integerGreaterThanNumber(-1); + } + + if (this.fromApiJsonHelper.parameterExists(toPeriodParamName, element)) { + toPeriod = this.fromApiJsonHelper.extractIntegerNamed(toPeriodParamName, element, locale); + baseDataValidator.reset().parameter(toPeriodParamName).value(toPeriod).notNull().integerGreaterThanNumber(-1); + } + + if (fromPeriod != null && toPeriod != null) { + if (fromPeriod > toPeriod) { + baseDataValidator.parameter(fromPeriodParamName).value(fromPeriod).failWithCode("fromperiod.greater.than.toperiod"); + } + } + BigDecimal amountRangeFrom = null; + BigDecimal amountRangeTo = null; + if (this.fromApiJsonHelper.parameterExists(amountRangeFromParamName, element)) { + amountRangeFrom = this.fromApiJsonHelper.extractBigDecimalNamed(amountRangeFromParamName, element, locale); + baseDataValidator.reset().parameter(amountRangeFromParamName).value(amountRangeFrom).notNull().positiveAmount(); + } + + if (this.fromApiJsonHelper.parameterExists(amountRangeToParamName, element)) { + amountRangeTo = this.fromApiJsonHelper.extractBigDecimalNamed(amountRangeToParamName, element, locale); + baseDataValidator.reset().parameter(amountRangeToParamName).value(amountRangeTo).notNull().positiveAmount(); + } + + if (amountRangeFrom != null && amountRangeTo != null) { + if (amountRangeFrom.compareTo(amountRangeTo) > 1) { + baseDataValidator.parameter(fromPeriodParamName).value(fromPeriod).failWithCode("fromperiod.greater.than.toperiod"); + } + } + + if (this.fromApiJsonHelper.parameterExists(annualInterestRateParamName, element)) { + final BigDecimal annualInterestRate = this.fromApiJsonHelper.extractBigDecimalNamed(annualInterestRateParamName, element, + locale); + baseDataValidator.reset().parameter(annualInterestRateParamName).value(annualInterestRate).notNull().zeroOrPositiveAmount(); + } + + if (this.fromApiJsonHelper.parameterExists(currencyCodeParamName, element)) { + final String currencyCode = this.fromApiJsonHelper.extractStringNamed(currencyCodeParamName, element); + baseDataValidator.reset().parameter(currencyCodeParamName).value(currencyCode).notBlank().notExceedingLengthOf(3); + } + + } + + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartSlabRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartSlabRepository.java new file mode 100644 index 000000000..245b99a92 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartSlabRepository.java @@ -0,0 +1,14 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.data; + +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChartSlab; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface InterestRateChartSlabRepository extends JpaRepository, JpaSpecificationExecutor { + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartSlabRepositoryWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartSlabRepositoryWrapper.java new file mode 100644 index 000000000..45a91f5cf --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/data/InterestRateChartSlabRepositoryWrapper.java @@ -0,0 +1,53 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.data; + +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChartSlab; +import org.mifosplatform.portfolio.interestratechart.exception.InterestRateChartSlabNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + *

+ * Wrapper for {@link InterestRateChartSlabRepository} that is responsible for checking + * if {@link InterestRateChartSlab} is returned when using findOne + * repository method and throwing an appropriate not found exception. + *

+ * + *

+ * This is to avoid need for checking and throwing in multiple areas of code + * base where {@link InterestRateChartSlabRepository} is required. + *

+ */ +@Service +public class InterestRateChartSlabRepositoryWrapper { + + private final InterestRateChartSlabRepository repository; + + @Autowired + public InterestRateChartSlabRepositoryWrapper(final InterestRateChartSlabRepository repository) { + this.repository = repository; + } + + public InterestRateChartSlab findOneWithNotFoundDetection(final Long chartSlabId) { + final InterestRateChartSlab chartSlab = this.repository.findOne(chartSlabId); + if (chartSlab == null) { throw new InterestRateChartSlabNotFoundException(chartSlabId); } + return chartSlab; + } + + public void save(final InterestRateChartSlab chartSlab) { + this.repository.save(chartSlab); + } + + public void delete(final InterestRateChartSlab chartSlab) { + this.repository.delete(chartSlab); + } + + public void saveAndFlush(final InterestRateChartSlab chartSlab) { + this.repository.saveAndFlush(chartSlab); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/domain/InterestRateChart.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/domain/InterestRateChart.java new file mode 100644 index 000000000..7005b1902 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/domain/InterestRateChart.java @@ -0,0 +1,261 @@ +/** + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * This Source Code Form is subject to the terms of the Mozilla Public + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.domain; + +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.INTERESTRATE_CHART_RESOURCE_NAME; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.amountRangeFromParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.amountRangeToParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.annualInterestRateParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.deleteParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.descriptionParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.fromPeriodParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.idParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.periodTypeParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.toPeriodParamName; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.persistence.CascadeType; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants; +import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; +import org.springframework.data.jpa.domain.AbstractPersistable; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +@Entity +@Table(name = "m_interest_rate_chart") +public class InterestRateChart extends AbstractPersistable { + + @Embedded + private InterestRateChartFields chartFields; + + @OneToMany(mappedBy = "interestRateChart", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) + private Set chartSlabs = new HashSet(); + + protected InterestRateChart() { + // + } + + public static InterestRateChart createNew(InterestRateChartFields chartFields, + Collection interestRateChartSlabs) { + + return new InterestRateChart(chartFields, new HashSet(interestRateChartSlabs)); + } + + private InterestRateChart(InterestRateChartFields chartFields, + Set interestRateChartSlabs) { + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(INTERESTRATE_CHART_RESOURCE_NAME); + + // validate before setting the other fields + this.validateChartSlabs(baseDataValidator); + this.throwExceptionIfValidationWarningsExist(dataValidationErrors); + + this.chartFields = chartFields; + this.addChartSlabs(interestRateChartSlabs); + + } + + public void validateChartSlabs(DataValidatorBuilder baseDataValidator) { + Collection chartSlabs = this.setOfChartSlabs(); + + Integer tmpPeriodType = null; + List chartSlabsList = new ArrayList(chartSlabs); + + for (int i = 0; i < chartSlabsList.size(); i++) { + InterestRateChartSlab iSlabs = chartSlabsList.get(i); + if (tmpPeriodType == null) { + tmpPeriodType = iSlabs.slabFields().periodType(); + } else if (!iSlabs.slabFields().periodType().equals(tmpPeriodType)) { + baseDataValidator.parameter(periodTypeParamName).value(iSlabs.slabFields().periodType()) + .failWithCode("period.type.is.not.same", tmpPeriodType); + } + for (int j = i + 1; j < chartSlabsList.size(); j++) { + InterestRateChartSlab jSlabs = chartSlabsList.get(j); + if (iSlabs.slabFields().isPeriodOverlapping(jSlabs.slabFields())) { + baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.period.overlapping", iSlabs.slabFields() + .fromPeriod(), iSlabs.slabFields().toPeriod(), jSlabs.slabFields().fromPeriod(), jSlabs + .slabFields().toPeriod()); + } + } + } + } + + public void addChartSlabs(Collection interestRateChartSlabsSet) { + Set existingChartSlabs = setOfChartSlabs(); + for (InterestRateChartSlab newChartSlabs : interestRateChartSlabsSet) { + newChartSlabs.setInterestRateChart(this); + existingChartSlabs.add(newChartSlabs); + } + } + + public void addChartSlab(InterestRateChartSlab newChartSlab) { + newChartSlab.setInterestRateChart(this); + setOfChartSlabs().add(newChartSlab); + } + + public Set setOfChartSlabs() { + if (this.chartSlabs == null) { + this.chartSlabs = new HashSet(); + } + return this.chartSlabs; + } + + public void update(JsonCommand command, final Map actualChanges) { + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(INTERESTRATE_CHART_RESOURCE_NAME); + + this.update(command, actualChanges, baseDataValidator, null, null); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + + } + + public void update(JsonCommand command, final Map actualChanges, final DataValidatorBuilder baseDataValidator, + final Set existingCharts, String currencyCode) { + + this.chartFields.update(command, actualChanges, baseDataValidator); + + // interestRateChartSlabs + if (command.hasParameter(InterestRateChartApiConstants.chartSlabs)) { + updateChartSlabs(command, actualChanges, baseDataValidator, currencyCode); + } + + this.validateCharts(baseDataValidator, existingCharts); + } + + private void validateCharts(final DataValidatorBuilder baseDataValidator, final Set existingCharts) { + + for (InterestRateChart existingChart : existingCharts) { + if (!existingChart.equals(this)) { + if (this.chartFields.isOverlapping(existingChart.chartFields)) { + baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.overlapping.from.and.end.dates", + existingChart.getFromDateAsLocalDate(), existingChart.getEndDateAsLocalDate(), this.getFromDateAsLocalDate(), + this.getEndDateAsLocalDate()); + } + } + } + } + + public void updateChartSlabs(JsonCommand command, final Map actualChanges, + final DataValidatorBuilder baseDataValidator, String currencyCode) { + + final Map deleteChartSlabs = new HashMap(); + final Map chartSlabsChanges = new HashMap(); + + if (command.hasParameter(InterestRateChartApiConstants.chartSlabs)) { + final JsonArray array = command.arrayOfParameterNamed(InterestRateChartApiConstants.chartSlabs); + if (array != null) { + for (int i = 0; i < array.size(); i++) { + final JsonObject chartSlabsElement = array.get(i).getAsJsonObject(); + JsonCommand chartSlabsCommand = JsonCommand.fromExistingCommand(command, chartSlabsElement); + if (chartSlabsCommand.parameterExists(idParamName)) { + final Long chartSlabId = chartSlabsCommand.longValueOfParameterNamed(idParamName); + final InterestRateChartSlab chartSlab = this.findChartSlab(chartSlabId); + if (chartSlab == null) { + baseDataValidator.parameter(idParamName).value(chartSlabId) + .failWithCode("no.chart.slab.associated.with.id"); + } else if (chartSlabsCommand.parameterExists(deleteParamName)) { + if (this.removeChartSlab(chartSlab)) { + deleteChartSlabs.put(idParamName, chartSlabId); + } + } else { + chartSlab.update(chartSlabsCommand, chartSlabsChanges, baseDataValidator); + } + } else { + + /** + * TODO: AA: Move this code to + * InterestRateChartSlabAssembler + */ + final String description = chartSlabsCommand.stringValueOfParameterNamed(descriptionParamName); + final Integer periodTypeId = chartSlabsCommand.integerValueOfParameterNamed(periodTypeParamName); + final SavingsPeriodFrequencyType periodFrequencyType = SavingsPeriodFrequencyType.fromInt(periodTypeId); + final Integer fromPeriod = chartSlabsCommand.integerValueOfParameterNamed(fromPeriodParamName); + final Integer toPeriod = chartSlabsCommand.integerValueOfParameterNamed(toPeriodParamName); + final BigDecimal amountRangeFrom = chartSlabsCommand.bigDecimalValueOfParameterNamed(amountRangeFromParamName); + final BigDecimal amountRangeTo = chartSlabsCommand.bigDecimalValueOfParameterNamed(amountRangeToParamName); + final BigDecimal annualInterestRate = chartSlabsCommand + .bigDecimalValueOfParameterNamed(annualInterestRateParamName); + final InterestRateChartSlabFields slabFields = InterestRateChartSlabFields.createNew(description, periodFrequencyType, + fromPeriod, toPeriod, amountRangeFrom, amountRangeTo, annualInterestRate, currencyCode); + final InterestRateChartSlab chartSlab = InterestRateChartSlab.createNew(slabFields, this); + chartSlab.slabFields().validateChartSlabPlatformRules(chartSlabsCommand, baseDataValidator); + this.addChartSlab(chartSlab); + } + } + } + } + + // add chart slab changes to actual changes list. + if (!chartSlabsChanges.isEmpty()) { + actualChanges.put(InterestRateChartApiConstants.chartSlabs, chartSlabsChanges); + } + + // add deleted chart Slabs to actual changes + if (!deleteChartSlabs.isEmpty()) { + actualChanges.put("deletedChartSlabs", deleteChartSlabs); + } + + this.validateChartSlabs(baseDataValidator); + } + + public InterestRateChartSlab findChartSlab(Long chartSlabId) { + final Set chartSlabs = setOfChartSlabs(); + + for (InterestRateChartSlab interestRateChartSlab : chartSlabs) { + if (interestRateChartSlab.getId().equals(chartSlabId)) { return interestRateChartSlab; } + } + return null; + } + + private boolean removeChartSlab(InterestRateChartSlab chartSlab) { + final Set chartSlabs = setOfChartSlabs(); + return chartSlabs.remove(chartSlab); + } + + public LocalDate getFromDateAsLocalDate() { + return this.chartFields.getFromDateAsLocalDate(); + } + + public LocalDate getEndDateAsLocalDate() { + return this.chartFields.getEndDateAsLocalDate(); + } + + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + public InterestRateChartFields chartFields() { + return this.chartFields; + } + + public boolean isApplicableChartFor(final LocalDate target){ + return this.chartFields.isApplicableChartFor(target); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/domain/InterestRateChartFields.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/domain/InterestRateChartFields.java new file mode 100644 index 000000000..17f470d36 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/domain/InterestRateChartFields.java @@ -0,0 +1,150 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.domain; + +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.dateFormatParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.descriptionParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.endDateParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.fromDateParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.localeParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.nameParamName; + +import java.util.Date; +import java.util.Map; + +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.domain.LocalDateInterval; +import org.mifosplatform.infrastructure.core.service.DateUtils; + +@Embeddable +public class InterestRateChartFields { + + @Column(name = "name", length = 100, unique = false, nullable = false) + private String name; + + @Column(name = "description", nullable = true) + private String description; + + @Temporal(TemporalType.DATE) + @Column(name = "from_date", nullable = false) + private Date fromDate; + + @Temporal(TemporalType.DATE) + @Column(name = "end_date", nullable = false) + private Date endDate; + + protected InterestRateChartFields() { + // + } + + public static InterestRateChartFields createNew(String name, String description, LocalDate fromDate, LocalDate toDate) { + return new InterestRateChartFields(name, description, fromDate, toDate); + } + + private InterestRateChartFields(String name, String description, LocalDate fromDate, LocalDate toDate) { + this.name = name; + this.description = description; + this.fromDate = fromDate.toDate(); + this.endDate = (toDate == null) ? null : toDate.toDate(); + } + + public void update(JsonCommand command, final Map actualChanges, final DataValidatorBuilder baseDataValidator) { + + if (command.isChangeInStringParameterNamed(nameParamName, this.name)) { + final String newValue = command.stringValueOfParameterNamed(nameParamName); + actualChanges.put(nameParamName, newValue); + this.name = newValue; + } + + if (command.isChangeInStringParameterNamed(descriptionParamName, this.description)) { + final String newValue = command.stringValueOfParameterNamed(descriptionParamName); + actualChanges.put(descriptionParamName, newValue); + this.description = newValue; + } + + final String localeAsInput = command.locale(); + final String dateFormat = command.dateFormat(); + + if (command.isChangeInLocalDateParameterNamed(fromDateParamName, getFromDateAsLocalDate())) { + final LocalDate newValue = command.localDateValueOfParameterNamed(fromDateParamName); + final String newValueAsString = command.stringValueOfParameterNamed(fromDateParamName); + actualChanges.put(fromDateParamName, newValueAsString); + actualChanges.put(localeParamName, localeAsInput); + actualChanges.put(dateFormatParamName, dateFormat); + this.fromDate = newValue.toDate(); + } + + if (command.isChangeInLocalDateParameterNamed(endDateParamName, getFromDateAsLocalDate())) { + final LocalDate newValue = command.localDateValueOfParameterNamed(endDateParamName); + final String newValueAsString = command.stringValueOfParameterNamed(endDateParamName); + actualChanges.put(endDateParamName, newValueAsString); + actualChanges.put(localeParamName, localeAsInput); + actualChanges.put(dateFormatParamName, dateFormat); + this.endDate = newValue.toDate(); + } + + if (isFromDateAfterToDate()) { + baseDataValidator.parameter(fromDateParamName).value(fromDate).failWithCode("from.date.is.after.to.date"); + } + } + + public boolean isFromDateAfterToDate() { + return isFromDateAfter(getEndDateAsLocalDate()); + } + + public boolean isFromDateAfter(LocalDate compare) { + final LocalDate fromDate = getFromDateAsLocalDate(); + if (fromDate != null && compare != null) { return fromDate.isAfter(compare); } + return false; + } + + public LocalDate getFromDateAsLocalDate() { + LocalDate fromDate = null; + if (this.fromDate != null) { + fromDate = new LocalDate(this.fromDate); + } + return fromDate; + } + + public LocalDate getEndDateAsLocalDate() { + LocalDate endDate = null; + if (this.endDate != null) { + endDate = new LocalDate(this.endDate); + } + return endDate; + } + + public boolean isOverlapping(InterestRateChartFields that) { + final LocalDate thisFromDate = this.getFromDateAsLocalDate(); + LocalDate thisEndDate = this.getEndDateAsLocalDate(); + thisEndDate = thisEndDate == null ? DateUtils.getLocalDateOfTenant() : thisEndDate; + final LocalDate thatFromDate = that.getFromDateAsLocalDate(); + LocalDate thatEndDate = that.getEndDateAsLocalDate(); + thatEndDate = thatEndDate == null ? DateUtils.getLocalDateOfTenant() : thatEndDate; + + final LocalDateInterval thisInterval = LocalDateInterval.create(thisFromDate, thisEndDate); + final LocalDateInterval thatInterval = LocalDateInterval.create(thatFromDate, thatEndDate); + + if(thisInterval.containsPortionOf(thatInterval) || thisInterval.contains(thatInterval)){ + return true; + } + return false;// no overlapping + } + + public boolean isApplicableChartFor(final LocalDate target){ + final LocalDate endDate = this.endDate == null ? DateUtils.getLocalDateOfTenant() : this.getEndDateAsLocalDate(); + final LocalDateInterval interval = LocalDateInterval.create(getFromDateAsLocalDate(), endDate); + return interval.contains(target); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/domain/InterestRateChartSlab.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/domain/InterestRateChartSlab.java new file mode 100644 index 000000000..792754e16 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/domain/InterestRateChartSlab.java @@ -0,0 +1,79 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.domain; + +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartSlabApiConstants.INTERESTRATE_CHART_SLAB_RESOURCE_NAME; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "m_interest_rate_slab") +public class InterestRateChartSlab extends AbstractPersistable { + + @Embedded + private InterestRateChartSlabFields slabFields; + + @ManyToOne(optional = false) + @JoinColumn(name = "interest_rate_chart_id", referencedColumnName = "id", nullable = false) + private InterestRateChart interestRateChart; + + protected InterestRateChartSlab() { + // + } + + public static InterestRateChartSlab createNew(InterestRateChartSlabFields slabFields, InterestRateChart interestRateChart) { + return new InterestRateChartSlab(slabFields, interestRateChart); + } + + public void update(JsonCommand command, final Map actualChanges) { + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(INTERESTRATE_CHART_SLAB_RESOURCE_NAME); + this.update(command, actualChanges, baseDataValidator); + this.interestRateChart.validateChartSlabs(baseDataValidator); + this.throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public void update(JsonCommand command, final Map actualChanges, final DataValidatorBuilder baseDataValidator) { + this.slabFields.update(command, actualChanges, baseDataValidator); + } + + private InterestRateChartSlab(InterestRateChartSlabFields slabFields, InterestRateChart interestRateChart) { + + this.slabFields = slabFields; + this.interestRateChart = interestRateChart; + + if (this.interestRateChart != null) { + interestRateChart.addChartSlab(this); + } + } + + public void setInterestRateChart(InterestRateChart interestRateChart) { + this.interestRateChart = interestRateChart; + } + + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + public InterestRateChartSlabFields slabFields(){ + return this.slabFields; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/domain/InterestRateChartSlabFields.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/domain/InterestRateChartSlabFields.java new file mode 100644 index 000000000..e9949ae83 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/domain/InterestRateChartSlabFields.java @@ -0,0 +1,213 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.domain; + +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.amountRangeFromParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.amountRangeToParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.annualInterestRateParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.descriptionParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.fromPeriodParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.periodTypeParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.toPeriodParamName; + +import java.math.BigDecimal; +import java.util.Map; + +import javax.persistence.Column; +import javax.persistence.Embeddable; + +import org.joda.time.Days; +import org.joda.time.LocalDate; +import org.joda.time.Months; +import org.joda.time.Weeks; +import org.joda.time.Years; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; + +@Embeddable +public class InterestRateChartSlabFields { + + @Column(name = "description", nullable = true) + private String description; + + @Column(name = "period_type_enum", nullable = false) + private Integer periodType; + + @Column(name = "from_period", nullable = false) + private Integer fromPeriod; + + @Column(name = "to_period", nullable = true) + private Integer toPeriod; + + @Column(name = "amount_range_from", scale = 6, precision = 19) + private BigDecimal amountRangeFrom; + + @Column(name = "amount_range_to", scale = 6, precision = 19) + private BigDecimal amountRangeTo; + + @Column(name = "annual_interest_rate", scale = 6, precision = 19, nullable = false) + private BigDecimal annualInterestRate; + + @Column(name = "currency_code", nullable = false) + private String currencyCode; + + protected InterestRateChartSlabFields() { + // + } + + public static InterestRateChartSlabFields createNew(String description, final SavingsPeriodFrequencyType periodFrequencyType, Integer fromPeriod, Integer toPeriod, + BigDecimal amountRangeFrom, BigDecimal amountRangeTo, BigDecimal annualInterestRate, String currencyCode) { + return new InterestRateChartSlabFields(description, periodFrequencyType, fromPeriod, toPeriod, amountRangeFrom, amountRangeTo, + annualInterestRate, currencyCode); + } + + private InterestRateChartSlabFields(String description, final SavingsPeriodFrequencyType periodFrequencyType, Integer fromPeriod, Integer toPeriod, + BigDecimal amountRangeFrom, BigDecimal amountRangeTo, BigDecimal annualInterestRate, String currencyCode) { + this.description = description; + this.periodType = (periodFrequencyType == null) ? null : periodFrequencyType.getValue(); + this.fromPeriod = fromPeriod; + this.toPeriod = toPeriod; + this.amountRangeFrom = amountRangeFrom; + this.amountRangeTo = amountRangeTo; + this.annualInterestRate = annualInterestRate; + this.currencyCode = currencyCode; + } + + public void update(JsonCommand command, final Map actualChanges, final DataValidatorBuilder baseDataValidator) { + + if (command.isChangeInStringParameterNamed(descriptionParamName, this.description)) { + final String newValue = command.stringValueOfParameterNamed(descriptionParamName); + actualChanges.put(descriptionParamName, newValue); + this.description = newValue; + } + + if (command.isChangeInIntegerParameterNamed(periodTypeParamName, periodType)) { + final Integer newValue = command.integerValueOfParameterNamed(periodTypeParamName); + actualChanges.put(periodTypeParamName, newValue); + this.periodType = newValue; + } + + if (command.isChangeInIntegerParameterNamed(fromPeriodParamName, fromPeriod)) { + final Integer newValue = command.integerValueOfParameterNamed(fromPeriodParamName); + actualChanges.put(fromPeriodParamName, newValue); + this.fromPeriod = newValue; + } + + if (command.isChangeInIntegerParameterNamed(toPeriodParamName, toPeriod)) { + final Integer newValue = command.integerValueOfParameterNamed(toPeriodParamName); + actualChanges.put(toPeriodParamName, newValue); + this.toPeriod = newValue; + } + + if (command.isChangeInBigDecimalParameterNamed(amountRangeFromParamName, amountRangeFrom)) { + final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(amountRangeFromParamName); + actualChanges.put(amountRangeFromParamName, newValue); + this.amountRangeFrom = newValue; + } + + if (command.isChangeInBigDecimalParameterNamed(amountRangeToParamName, amountRangeTo)) { + final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(amountRangeToParamName); + actualChanges.put(amountRangeToParamName, newValue); + this.amountRangeTo = newValue; + } + + if (command.isChangeInBigDecimalParameterNamed(annualInterestRateParamName, annualInterestRate)) { + final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(annualInterestRateParamName); + actualChanges.put(annualInterestRateParamName, newValue); + this.annualInterestRate = newValue; + } + + this.validateChartSlabPlatformRules(command, baseDataValidator); + } + + public void validateChartSlabPlatformRules(JsonCommand chartSlabsCommand, DataValidatorBuilder baseDataValidator) { + if (this.isFromPeriodGreaterThanToPeriod()) { + final Integer fromPeriod = chartSlabsCommand.integerValueOfParameterNamed(fromPeriodParamName); + baseDataValidator.parameter(fromPeriodParamName).value(fromPeriod).failWithCode("from.period.is.greater.than.to.period"); + } + + if (this.isAmountRangeFromGreaterThanTo()) { + final BigDecimal amountRangeFrom = chartSlabsCommand.bigDecimalValueOfParameterNamed(amountRangeFromParamName); + baseDataValidator.parameter(amountRangeFromParamName).value(amountRangeFrom) + .failWithCode("amount.range.from.is.greater.than.amount.range.to"); + } + } + + public boolean isFromPeriodGreaterThanToPeriod() { + boolean isGreater = false; + if (this.toPeriod != null && fromPeriod.compareTo(this.toPeriod) > 1) { + isGreater = true; + } + return isGreater; + } + + public boolean isAmountRangeFromGreaterThanTo() { + boolean isGreater = false; + if (this.amountRangeFrom != null && this.amountRangeTo != null && this.amountRangeFrom.compareTo(this.amountRangeTo) > 1) { + isGreater = true; + } + return isGreater; + } + + public Integer periodType() { + return this.periodType; + } + + public Integer fromPeriod() { + return this.fromPeriod; + } + + public Integer toPeriod() { + return this.toPeriod; + } + + public boolean isPeriodOverlapping(InterestRateChartSlabFields that) { + if (that.toPeriod == null){ + if (this.toPeriod == null){ + return true; + } + return that.fromPeriod <= this.toPeriod; + } + return this.fromPeriod <= that.toPeriod && that.fromPeriod <= this.toPeriod; + } + + public boolean isBetweenPeriod(final LocalDate periodStartDate, final LocalDate periodEndDate){ + final Integer compare = depositPeriod(periodStartDate, periodEndDate); + return (compare < this.fromPeriod || (this.toPeriod != null && compare > this.toPeriod)) ? false : true; + } + + public boolean isAmountRangeProvided(){ + return (this.amountRangeFrom == null) ? false : true; + } + + public BigDecimal annualInterestRate() { + return this.annualInterestRate; + } + + public Integer depositPeriod(final LocalDate periodStartDate, final LocalDate periodEndDate) { + Integer actualDepositPeriod = 0; + final SavingsPeriodFrequencyType periodFrequencyType = SavingsPeriodFrequencyType.fromInt(this.periodType()); + switch (periodFrequencyType) { + case DAYS: + actualDepositPeriod = Days.daysBetween(periodStartDate, periodEndDate).getDays(); + break; + case WEEKS: + actualDepositPeriod = Weeks.weeksBetween(periodStartDate, periodEndDate).getWeeks(); + break; + case MONTHS: + actualDepositPeriod = Months.monthsBetween(periodStartDate, periodEndDate).getMonths(); + break; + case YEARS: + actualDepositPeriod = Years.yearsBetween(periodStartDate, periodEndDate).getYears(); + break; + case INVALID: + actualDepositPeriod = 0;// default value + break; + } + return actualDepositPeriod; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/exception/InterestRateChartNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/exception/InterestRateChartNotFoundException.java new file mode 100644 index 000000000..2da92a592 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/exception/InterestRateChartNotFoundException.java @@ -0,0 +1,15 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +public class InterestRateChartNotFoundException extends AbstractPlatformResourceNotFoundException { + + public InterestRateChartNotFoundException(final Long id) { + super("error.msg.interest.rate.chart.id.invalid", "Interest rate chart with identifier " + id + " does not exist", id); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/exception/InterestRateChartSlabNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/exception/InterestRateChartSlabNotFoundException.java new file mode 100644 index 000000000..cde671791 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/exception/InterestRateChartSlabNotFoundException.java @@ -0,0 +1,19 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +public class InterestRateChartSlabNotFoundException extends AbstractPlatformResourceNotFoundException { + + public InterestRateChartSlabNotFoundException(final Long id) { + super("error.msg.interest.rate.chart.slab.id.invalid", "Interest rate chart slab with identifier " + id + " does not exist", id); + } + + public InterestRateChartSlabNotFoundException(final Long id, final Long chartId) { + super("error.msg.interest.rate.chart.slab.id.invalid", "Interest rate chart slab with identifier " + id + " does not exist in interest chart with identifier " + chartId , id, chartId); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/CreateInterestRateChartCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/CreateInterestRateChartCommandHandler.java new file mode 100644 index 000000000..5b0c9ba2b --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/CreateInterestRateChartCommandHandler.java @@ -0,0 +1,29 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class CreateInterestRateChartCommandHandler implements NewCommandSourceHandler { + + private final InterestRateChartWritePlatformService writePlatformService; + + @Autowired + public CreateInterestRateChartCommandHandler(final InterestRateChartWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.create(command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/CreateInterestRateChartSlabCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/CreateInterestRateChartSlabCommandHandler.java new file mode 100644 index 000000000..dfe743153 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/CreateInterestRateChartSlabCommandHandler.java @@ -0,0 +1,29 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartSlabWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class CreateInterestRateChartSlabCommandHandler implements NewCommandSourceHandler { + + private final InterestRateChartSlabWritePlatformService writePlatformService; + + @Autowired + public CreateInterestRateChartSlabCommandHandler(final InterestRateChartSlabWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.create(command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/DeleteInterestRateChartCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/DeleteInterestRateChartCommandHandler.java new file mode 100644 index 000000000..50a75621c --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/DeleteInterestRateChartCommandHandler.java @@ -0,0 +1,29 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class DeleteInterestRateChartCommandHandler implements NewCommandSourceHandler { + + private final InterestRateChartWritePlatformService writePlatformService; + + @Autowired + public DeleteInterestRateChartCommandHandler(final InterestRateChartWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.deleteChart(command.entityId()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/DeleteInterestRateChartSlabCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/DeleteInterestRateChartSlabCommandHandler.java new file mode 100644 index 000000000..b39321d02 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/DeleteInterestRateChartSlabCommandHandler.java @@ -0,0 +1,29 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartSlabWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class DeleteInterestRateChartSlabCommandHandler implements NewCommandSourceHandler { + + private final InterestRateChartSlabWritePlatformService writePlatformService; + + @Autowired + public DeleteInterestRateChartSlabCommandHandler(final InterestRateChartSlabWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.deleteChartSlab(command.entityId(), command.interestRateChartId()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/UpdateInterestRateChartCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/UpdateInterestRateChartCommandHandler.java new file mode 100644 index 000000000..f29205ea3 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/UpdateInterestRateChartCommandHandler.java @@ -0,0 +1,29 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class UpdateInterestRateChartCommandHandler implements NewCommandSourceHandler { + + private final InterestRateChartWritePlatformService writePlatformService; + + @Autowired + public UpdateInterestRateChartCommandHandler(final InterestRateChartWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.update(command.entityId(), command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/UpdateInterestRateChartSlabCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/UpdateInterestRateChartSlabCommandHandler.java new file mode 100644 index 000000000..d998c9480 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/handler/UpdateInterestRateChartSlabCommandHandler.java @@ -0,0 +1,29 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartSlabWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class UpdateInterestRateChartSlabCommandHandler implements NewCommandSourceHandler { + + private final InterestRateChartSlabWritePlatformService writePlatformService; + + @Autowired + public UpdateInterestRateChartSlabCommandHandler(final InterestRateChartSlabWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.update(command.entityId(), command.interestRateChartId(), command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartAssembler.java new file mode 100644 index 000000000..2f6ce3ba1 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartAssembler.java @@ -0,0 +1,94 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.service; + +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.INTERESTRATE_CHART_RESOURCE_NAME; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.descriptionParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.endDateParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.fromDateParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.nameParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartSlabApiConstants.currencyCodeParamName; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartRepositoryWrapper; +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChart; +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChartFields; +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChartSlab; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.google.gson.JsonElement; + +@Service +public class InterestRateChartAssembler { + + private final FromJsonHelper fromApiJsonHelper; + private final InterestRateChartRepositoryWrapper interestRateChartRepositoryWrapper; + private final InterestRateChartSlabAssembler chartSlabAssembler; + + @Autowired + public InterestRateChartAssembler(final FromJsonHelper fromApiJsonHelper, + final InterestRateChartRepositoryWrapper interestRateChartRepositoryWrapper, + final InterestRateChartSlabAssembler chartSlabAssembler) { + this.fromApiJsonHelper = fromApiJsonHelper; + this.interestRateChartRepositoryWrapper = interestRateChartRepositoryWrapper; + this.chartSlabAssembler = chartSlabAssembler; + } + + /** + * Assembles a new {@link InterestRateChart} from JSON Slabs passed in + * request + */ + public InterestRateChart assembleFrom(final JsonCommand command) { + + final JsonElement element = command.parsedJson(); + final List dataValidationErrors = new ArrayList(); + @SuppressWarnings("unused") + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(INTERESTRATE_CHART_RESOURCE_NAME); + final String currencyCode = this.fromApiJsonHelper.extractStringNamed(currencyCodeParamName, element); + final InterestRateChart newChart = this.assembleFrom(element, currencyCode); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + return newChart; + } + + public InterestRateChart assembleFrom(final JsonElement element, final String currencyCode) { + + final String name = this.fromApiJsonHelper.extractStringNamed(nameParamName, element); + final String description = this.fromApiJsonHelper.extractStringNamed(descriptionParamName, element); + final LocalDate fromDate = this.fromApiJsonHelper.extractLocalDateNamed(fromDateParamName, element); + final LocalDate toDate = this.fromApiJsonHelper.extractLocalDateNamed(endDateParamName, element); + + + // assemble chart Slabs + final Collection newChartSlabs = this.chartSlabAssembler.assembleChartSlabsFrom(element, + currencyCode); + + final InterestRateChartFields fields = InterestRateChartFields.createNew(name, description, fromDate, toDate); + final InterestRateChart newChart = InterestRateChart.createNew(fields, newChartSlabs); + return newChart; + } + + public InterestRateChart assembleFrom(final Long interestRateChartId) { + final InterestRateChart interestRateChart = this.interestRateChartRepositoryWrapper + .findOneWithNotFoundDetection(interestRateChartId); + return interestRateChart; + } + + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartDropdownReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartDropdownReadPlatformService.java new file mode 100644 index 000000000..f1d83b13c --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartDropdownReadPlatformService.java @@ -0,0 +1,15 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.service; + +import java.util.Collection; + +import org.mifosplatform.infrastructure.core.data.EnumOptionData; + +public interface InterestRateChartDropdownReadPlatformService { + + Collection retrievePeriodTypeOptions(); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartDropdownReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartDropdownReadPlatformServiceImpl.java new file mode 100644 index 000000000..0b5565d7c --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartDropdownReadPlatformServiceImpl.java @@ -0,0 +1,22 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.service; + +import java.util.Collection; + +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.portfolio.interestratechart.InterestRateChartPeriodType; +import org.springframework.stereotype.Service; + +@Service +public class InterestRateChartDropdownReadPlatformServiceImpl implements InterestRateChartDropdownReadPlatformService { + + @Override + public Collection retrievePeriodTypeOptions() { + return InterestRateChartEnumerations.periodType(InterestRateChartPeriodType.values()); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartEnumerations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartEnumerations.java new file mode 100644 index 000000000..dc43941b7 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartEnumerations.java @@ -0,0 +1,57 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.service; + +import java.util.ArrayList; +import java.util.List; + +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.portfolio.interestratechart.InterestRateChartPeriodType; + +public class InterestRateChartEnumerations { + + public static EnumOptionData periodType(final Integer type) { + return periodType(InterestRateChartPeriodType.fromInt(type)); + } + + public static EnumOptionData periodType(final InterestRateChartPeriodType type) { + EnumOptionData optionData = new EnumOptionData(InterestRateChartPeriodType.INVALID.getValue().longValue(), + InterestRateChartPeriodType.INVALID.getCode(), "Invalid"); + + switch (type) { + case INVALID: + break; + case DAYS: + optionData = new EnumOptionData(InterestRateChartPeriodType.DAYS.getValue().longValue(), + InterestRateChartPeriodType.DAYS.getCode(), "Days"); + break; + case WEEKS: + optionData = new EnumOptionData(InterestRateChartPeriodType.WEEKS.getValue().longValue(), + InterestRateChartPeriodType.WEEKS.getCode(), "Weeks"); + break; + case MONTHS: + optionData = new EnumOptionData(InterestRateChartPeriodType.MONTHS.getValue().longValue(), + InterestRateChartPeriodType.MONTHS.getCode(), "Months"); + break; + case YEARS: + optionData = new EnumOptionData(InterestRateChartPeriodType.YEARS.getValue().longValue(), + InterestRateChartPeriodType.YEARS.getCode(), "Years"); + break; + } + + return optionData; + } + + public static List periodType(final InterestRateChartPeriodType[] periodTypes) { + final List optionDatas = new ArrayList(); + for (final InterestRateChartPeriodType periodType : periodTypes) { + if (!periodType.isInvalid()) { + optionDatas.add(periodType(periodType)); + } + } + return optionDatas; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartReadPlatformService.java new file mode 100644 index 000000000..920f06cbd --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartReadPlatformService.java @@ -0,0 +1,33 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.service; + +import java.util.Collection; + +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartData; + +public interface InterestRateChartReadPlatformService { + + //Collection retrieveAll(Long savingsProductId); + + InterestRateChartData retrieveOne(Long interestChartId); + + //Collection retrieveAllWithSlabs(); + + Collection retrieveAllWithSlabs(Long savingsProductId); + + Collection retrieveAllWithSlabsWithTemplate(Long savingsProductId); + + InterestRateChartData retrieveOneWithSlabs(Long interestChartId); + + InterestRateChartData retrieveWithTemplate(InterestRateChartData interestRateChartData); + + InterestRateChartData retrieveOneWithSlabsOnProductId(Long productId); + + InterestRateChartData template(); + + InterestRateChartData retrieveActiveChartWithTemplate(Long productId); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartReadPlatformServiceImpl.java new file mode 100644 index 000000000..d93f94298 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartReadPlatformServiceImpl.java @@ -0,0 +1,279 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.service; + +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.infrastructure.core.domain.JdbcSupport; +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.interestratechart.data.InterestRateChartData; +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartSlabData; +import org.mifosplatform.portfolio.interestratechart.exception.InterestRateChartNotFoundException; +import org.mifosplatform.portfolio.savings.data.DepositProductData; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.ResultSetExtractor; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class InterestRateChartReadPlatformServiceImpl implements InterestRateChartReadPlatformService { + + private final PlatformSecurityContext context; + private final JdbcTemplate jdbcTemplate; + private final InterestRateChartMapper chartRowMapper = new InterestRateChartMapper(); + private final InterestRateChartExtractor chartExtractor = new InterestRateChartExtractor(); + private final InterestRateChartDropdownReadPlatformService chartDropdownReadPlatformService; + + @Autowired + public InterestRateChartReadPlatformServiceImpl(PlatformSecurityContext context, final RoutingDataSource dataSource, + InterestRateChartDropdownReadPlatformService chartDropdownReadPlatformService) { + this.context = context; + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.chartDropdownReadPlatformService = chartDropdownReadPlatformService; + } + + /* + * @Override public Collection retrieveAll(Long + * productId) { + * + * // TODO return null; } + */ + + @Override + public InterestRateChartData retrieveOne(Long chartId) { + try { + this.context.authenticatedUser(); + final String sql = "select " + this.chartRowMapper.schema() + " where irc.id = ?"; + return this.jdbcTemplate.queryForObject(sql, this.chartRowMapper, new Object[] { chartId }); + } catch (final EmptyResultDataAccessException e) { + throw new InterestRateChartNotFoundException(chartId); + } + } + + /* + * @Override public Collection + * retrieveAllWithSlabs() { this.context.authenticatedUser(); final String + * sql = "select " + this.chartExtractor.schema() + + * " order by irc.id, ircd.id "; return this.jdbcTemplate.query(sql, + * this.chartExtractor); } + */ + + @Override + public Collection retrieveAllWithSlabs(Long productId) { + this.context.authenticatedUser(); + String sql = "select " + this.chartExtractor.schema() + " where sp.id = ? order by irc.id, ircd.id"; + return this.jdbcTemplate.query(sql, this.chartExtractor, new Object[] { productId }); + } + + @Override + public Collection retrieveAllWithSlabsWithTemplate(Long productId) { + Collection chartDatas = new ArrayList(); + + for (InterestRateChartData chartData : retrieveAllWithSlabs(productId)) { + chartDatas.add(retrieveWithTemplate(chartData)); + } + + return chartDatas; + } + + @Override + public InterestRateChartData retrieveActiveChartWithTemplate(Long productId) { + Collection chartDatas = this.retrieveAllWithSlabsWithTemplate(productId); + return DepositProductData.activeChart(chartDatas); + } + + @Override + public InterestRateChartData retrieveOneWithSlabs(Long chartId) { + this.context.authenticatedUser(); + final String sql = "select " + this.chartExtractor.schema() + " where irc.id = ? order by ircd.id asc"; + Collection chartDatas = this.jdbcTemplate.query(sql, this.chartExtractor, new Object[] { chartId }); + if (chartDatas == null || chartDatas.isEmpty()) { throw new InterestRateChartNotFoundException(chartId); } + + return chartDatas.iterator().next(); + } + + @Override + public InterestRateChartData retrieveWithTemplate(InterestRateChartData chartData) { + return InterestRateChartData.withTemplate(chartData, this.chartDropdownReadPlatformService.retrievePeriodTypeOptions()); + } + + @Override + public InterestRateChartData retrieveOneWithSlabsOnProductId(@SuppressWarnings("unused") Long productId) { + // TODO Auto-generated method stub + return null; + } + + @Override + public InterestRateChartData template() { + return InterestRateChartData.template(this.chartDropdownReadPlatformService.retrievePeriodTypeOptions()); + } + + private static final class InterestRateChartExtractor implements ResultSetExtractor> { + + InterestRateChartMapper chartMapper = new InterestRateChartMapper(); + InterestRateChartSlabsMapper chartSlabsMapper = new InterestRateChartSlabsMapper(); + + private final String schemaSql; + + public String schema() { + return this.schemaSql; + } + + private InterestRateChartExtractor() { + final StringBuilder sqlBuilder = new StringBuilder(400); + + sqlBuilder + .append("irc.id as ircId, irc.name as ircName, irc.description as ircDescription,") + .append("irc.from_date as ircFromDate, irc.end_date as ircEndDate, ") + .append("ircd.id as ircdId, ircd.description as ircdDescription, ircd.period_type_enum ircdPeriodTypeId, ") + .append("ircd.from_period as ircdFromPeriod, ircd.to_period as ircdToPeriod, ircd.amount_range_from as ircdAmountRangeFrom, ") + .append("ircd.amount_range_to as ircdAmountRangeTo, ircd.annual_interest_rate as ircdAnnualInterestRate, ") + .append("curr.code as currencyCode, curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ") + .append("curr.display_symbol as currencyDisplaySymbol, curr.decimal_places as currencyDigits, curr.currency_multiplesof as inMultiplesOf, ") + .append("sp.id as savingsProductId, sp.name as savingsProductName ") + .append("from ") + .append("m_interest_rate_chart irc left join m_interest_rate_slab ircd on irc.id=ircd.interest_rate_chart_id ") + .append("left join m_currency curr on ircd.currency_code= curr.code ") + .append("left join m_deposit_product_interest_rate_chart dpirc on irc.id=dpirc.interest_rate_chart_id ") + .append("left join m_savings_product sp on sp.id=dpirc.deposit_product_id "); + + this.schemaSql = sqlBuilder.toString(); + } + + @Override + public Collection extractData(ResultSet rs) throws SQLException, DataAccessException { + + List chartDataList = new ArrayList(); + + InterestRateChartData chartData = null; + Long interestRateChartId = null; + int ircIndex = 0;// Interest rate chart index + int ircdIndex = 0;// Interest rate chart Slabs index + + while (rs.next()) { + Long tempIrcId = rs.getLong("ircId"); + // first row or when interest rate chart id changes + if (chartData == null || (interestRateChartId != null && !interestRateChartId.equals(tempIrcId))) { + + interestRateChartId = tempIrcId; + chartData = chartMapper.mapRow(rs, ircIndex++); + chartDataList.add(chartData); + ircdIndex = 0;// reset index + + } + final InterestRateChartSlabData chartSlabsData = chartSlabsMapper.mapRow(rs, ircdIndex++); + if (chartSlabsData != null) { + chartData.addChartSlab(chartSlabsData); + } + } + return chartDataList; + } + + } + + public static final class InterestRateChartMapper implements RowMapper { + + private final String schemaSql; + + public String schema() { + return this.schemaSql; + } + + private InterestRateChartMapper() { + final StringBuilder sqlBuilder = new StringBuilder(400); + + sqlBuilder.append("irc.id as ircId, irc.name as ircName, irc.description as ircDescription, ") + .append("irc.from_date as ircFromDate, irc.end_date as ircEndDate, irc.is_active_chart as isActiveChart, ") + .append("sp.id as savingsProductId, sp.name as savingsProductName ").append("from ") + .append("m_interest_rate_chart irc ") + .append("left join m_deposit_product_interest_rate_chart dpirc on irc.id=dpirc.interest_rate_chart_id ") + .append("left join m_savings_product sp on sp.id=dpirc.deposit_product_id "); + this.schemaSql = sqlBuilder.toString(); + } + + @Override + public InterestRateChartData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException { + final Long id = rs.getLong("ircId"); + final String name = rs.getString("ircName"); + final String description = rs.getString("ircDescription"); + final LocalDate fromDate = JdbcSupport.getLocalDate(rs, "ircFromDate"); + final LocalDate endDate = JdbcSupport.getLocalDate(rs, "ircEndDate"); + final Long savingsProductId = rs.getLong("savingsProductId"); + final String savingsProductName = rs.getString("savingsProductName"); + + return InterestRateChartData.instance(id, name, description, fromDate, endDate, savingsProductId, savingsProductName, null); + } + + } + + private static final class InterestRateChartSlabsMapper implements RowMapper { + + private final String schemaSql; + + @SuppressWarnings("unused") + public String schema() { + return this.schemaSql; + } + + private InterestRateChartSlabsMapper() { + final StringBuilder sqlBuilder = new StringBuilder(400); + + sqlBuilder + .append("ircd.id as ircdId, ircd.description as ircdDescription, ircd.period_type_enum ircdPeriodTypeId, ") + .append("ircd.from_period as ircdFromPeriod, ircd.to_period as ircdToPeriod, ircd.amount_range_from as ircdAmountRangeFrom, ") + .append("ircd.amount_range_to as ircdAmountRangeTo, ircd.annual_interest_rate as ircdAnnualInterestRate, ") + .append("curr.code as currencyCode, curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ") + .append("curr.display_symbol as currencyDisplaySymbol, curr.decimal_places as currencyDigits, curr.currency_multiplesof as inMultiplesOf ") + .append("from ").append("m_interest_rate_slab ircd ") + .append("left join m_currency curr on ircd.currency_code= curr.code "); + this.schemaSql = sqlBuilder.toString(); + } + + @Override + public InterestRateChartSlabData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException { + final Long id = JdbcSupport.getLongDefaultToNullIfZero(rs, "ircdId"); + // If there are not chart Slabs are associated then in + // InterestRateChartExtractor the chart Slabs id will be null. + if (id == null) { return null; } + + final String description = rs.getString("ircdDescription"); + final Integer fromPeriod = JdbcSupport.getInteger(rs, "ircdFromPeriod"); + final Integer toPeriod = JdbcSupport.getInteger(rs, "ircdToPeriod"); + final Integer periodTypeId = JdbcSupport.getInteger(rs, "ircdPeriodTypeId"); + final EnumOptionData periodType = InterestRateChartEnumerations.periodType(periodTypeId); + final BigDecimal amountRangeFrom = rs.getBigDecimal("ircdAmountRangeFrom"); + final BigDecimal amountRangeTo = rs.getBigDecimal("ircdAmountRangeTo"); + final BigDecimal annualInterestRate = rs.getBigDecimal("ircdAnnualInterestRate"); + + // currency Slabs + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + // currency + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, + currencyDisplaySymbol, currencyNameCode); + + return InterestRateChartSlabData.instance(id, description, periodType, fromPeriod, toPeriod, amountRangeFrom, amountRangeTo, + annualInterestRate, currency); + } + + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartSlabAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartSlabAssembler.java new file mode 100644 index 000000000..1b0cfb428 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartSlabAssembler.java @@ -0,0 +1,128 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.service; + +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.amountRangeFromParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.amountRangeToParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.annualInterestRateParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.chartSlabs; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.descriptionParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.fromPeriodParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.periodTypeParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.toPeriodParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartSlabApiConstants.INTERESTRATE_CHART_SLAB_RESOURCE_NAME; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartSlabApiConstants.currencyCodeParamName; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartRepositoryWrapper; +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChart; +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChartSlab; +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChartSlabFields; +import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +@Service +public class InterestRateChartSlabAssembler { + + private final FromJsonHelper fromApiJsonHelper; + private final InterestRateChartRepositoryWrapper interestRateChartRepositoryWrapper; + + @Autowired + public InterestRateChartSlabAssembler(final FromJsonHelper fromApiJsonHelper, + final InterestRateChartRepositoryWrapper interestRateChartRepositoryWrapper) { + this.fromApiJsonHelper = fromApiJsonHelper; + this.interestRateChartRepositoryWrapper = interestRateChartRepositoryWrapper; + } + + /** + * Assembles a new {@link InterestRateChartSlab} from JSON Slabs passed in + * request + */ + public InterestRateChartSlab assembleFrom(final JsonCommand command) { + + final JsonElement element = command.parsedJson(); + final JsonObject elementObject = element.getAsJsonObject(); + final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(elementObject); + final String currencyCode = command.stringValueOfParameterNamed(currencyCodeParamName); + + final Long chartId = command.interestRateChartId(); + + final InterestRateChart chart = this.interestRateChartRepositoryWrapper.findOneWithNotFoundDetection(chartId); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(INTERESTRATE_CHART_SLAB_RESOURCE_NAME); + + final InterestRateChartSlab newChartSlab = assembleChartSlabs(chart, elementObject, currencyCode, locale); + // validate chart Slabs + newChartSlab.slabFields().validateChartSlabPlatformRules(command, baseDataValidator); + chart.validateChartSlabs(baseDataValidator); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + return newChartSlab; + } + + private InterestRateChartSlab assembleChartSlabs(final InterestRateChart interestRateChart, final JsonElement element, + final String currencyCode, final Locale locale) { + final String description = this.fromApiJsonHelper.extractStringNamed(descriptionParamName, element); + final Integer periodTypeId = this.fromApiJsonHelper.extractIntegerNamed(periodTypeParamName, element, locale); + final SavingsPeriodFrequencyType periodType = SavingsPeriodFrequencyType.fromInt(periodTypeId); + final Integer fromPeriod = this.fromApiJsonHelper.extractIntegerNamed(fromPeriodParamName, element, locale); + final Integer toPeriod = this.fromApiJsonHelper.extractIntegerNamed(toPeriodParamName, element, locale); + final BigDecimal amountRangeFrom = this.fromApiJsonHelper.extractBigDecimalNamed(amountRangeFromParamName, element, locale); + final BigDecimal amountRangeTo = this.fromApiJsonHelper.extractBigDecimalNamed(amountRangeToParamName, element, locale); + final BigDecimal annualInterestRate = this.fromApiJsonHelper.extractBigDecimalNamed(annualInterestRateParamName, element, locale); + final InterestRateChartSlabFields slabFields = InterestRateChartSlabFields.createNew(description, periodType, fromPeriod, toPeriod, + amountRangeFrom, amountRangeTo, annualInterestRate, currencyCode); + return InterestRateChartSlab.createNew(slabFields, interestRateChart); + + } + + public InterestRateChartSlab assembleFrom(final Long chartSlabId, final Long chartId) { + final InterestRateChart chart = this.interestRateChartRepositoryWrapper.findOneWithNotFoundDetection(chartId); + final InterestRateChartSlab interestRateChartSlab = chart.findChartSlab(chartSlabId); + return interestRateChartSlab; + } + + public Collection assembleChartSlabsFrom(final JsonElement element, String currencyCode) { + final Collection interestRateChartSlabsSet = new HashSet(); + + if (element.isJsonObject()) { + final JsonObject topLevelJsonElement = element.getAsJsonObject(); + final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement); + if (topLevelJsonElement.has(chartSlabs) && topLevelJsonElement.get(chartSlabs).isJsonArray()) { + final JsonArray array = topLevelJsonElement.get(chartSlabs).getAsJsonArray(); + for (int i = 0; i < array.size(); i++) { + final JsonObject interstRateChartElement = array.get(i).getAsJsonObject(); + final InterestRateChartSlab chartSlab = this.assembleChartSlabs(null, interstRateChartElement, currencyCode, locale); + interestRateChartSlabsSet.add(chartSlab); + } + } + } + + return interestRateChartSlabsSet; + } + + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartSlabReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartSlabReadPlatformService.java new file mode 100644 index 000000000..b27e8d5fa --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartSlabReadPlatformService.java @@ -0,0 +1,19 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.service; + +import java.util.Collection; + +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartSlabData; + +public interface InterestRateChartSlabReadPlatformService { + + Collection retrieveAll(Long chartId); + + InterestRateChartSlabData retrieveOne(Long chartId, Long chartSlabId); + + InterestRateChartSlabData retrieveWithTemplate(InterestRateChartSlabData chartSlab); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartSlabReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartSlabReadPlatformServiceImpl.java new file mode 100644 index 000000000..a345ed8a8 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartSlabReadPlatformServiceImpl.java @@ -0,0 +1,119 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.service; + +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; + +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.infrastructure.core.domain.JdbcSupport; +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.interestratechart.data.InterestRateChartSlabData; +import org.mifosplatform.portfolio.interestratechart.exception.InterestRateChartSlabNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class InterestRateChartSlabReadPlatformServiceImpl implements InterestRateChartSlabReadPlatformService { + + private final PlatformSecurityContext context; + private final JdbcTemplate jdbcTemplate; + private final InterestRateChartSlabsMapper chartSlabRowMapper = new InterestRateChartSlabsMapper(); + private final InterestRateChartDropdownReadPlatformService chartDropdownReadPlatformService; + + @Autowired + public InterestRateChartSlabReadPlatformServiceImpl(PlatformSecurityContext context, final RoutingDataSource dataSource, + InterestRateChartDropdownReadPlatformService chartDropdownReadPlatformService) { + this.context = context; + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.chartDropdownReadPlatformService = chartDropdownReadPlatformService; + } + + @Override + public Collection retrieveAll(Long chartId) { + this.context.authenticatedUser(); + final String sql = "select " + this.chartSlabRowMapper.schema() + " where ircd.interest_rate_chart_id = ? order by ircd.id"; + return this.jdbcTemplate.query(sql, this.chartSlabRowMapper, new Object[] { chartId }); + } + + @Override + public InterestRateChartSlabData retrieveOne(Long chartId, Long chartSlabId) { + try { + this.context.authenticatedUser(); + final String sql = "select " + this.chartSlabRowMapper.schema() + " where ircd.interest_rate_chart_id = ? and ircd.id = ?"; + return this.jdbcTemplate.queryForObject(sql, this.chartSlabRowMapper, new Object[] { chartId, chartSlabId }); + } catch (final EmptyResultDataAccessException e) { + throw new InterestRateChartSlabNotFoundException(chartSlabId, chartId); + } + } + + @Override + public InterestRateChartSlabData retrieveWithTemplate(InterestRateChartSlabData chartSlab) { + return InterestRateChartSlabData.withTemplate(chartSlab, this.chartDropdownReadPlatformService.retrievePeriodTypeOptions()); + } + + private static final class InterestRateChartSlabsMapper implements RowMapper { + + private final String schemaSql; + + public String schema() { + return this.schemaSql; + } + + private InterestRateChartSlabsMapper() { + final StringBuilder sqlBuilder = new StringBuilder(400); + + sqlBuilder + .append("ircd.id as ircdId, ircd.description as ircdDescription, ircd.period_type_enum ircdPeriodTypeId, ") + .append("ircd.from_period as ircdFromPeriod, ircd.to_period as ircdToPeriod, ircd.amount_range_from as ircdAmountRangeFrom, ") + .append("ircd.amount_range_to as ircdAmountRangeTo, ircd.annual_interest_rate as ircdAnnualInterestRate, ") + .append("curr.code as currencyCode, curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ") + .append("curr.display_symbol as currencyDisplaySymbol, curr.decimal_places as currencyDigits, curr.currency_multiplesof as inMultiplesOf ") + .append("from ").append("m_interest_rate_slab ircd ") + .append("left join m_currency curr on ircd.currency_code= curr.code "); + this.schemaSql = sqlBuilder.toString(); + } + + @Override + public InterestRateChartSlabData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException { + final Long id = JdbcSupport.getLongDefaultToNullIfZero(rs, "ircdId"); + // If there are not chart Slabs are associated then in + // InterestRateChartExtractor the chart Slabs id will be null. + if (id == null) { return null; } + + final String description = rs.getString("ircdDescription"); + final Integer fromPeriod = JdbcSupport.getInteger(rs, "ircdFromPeriod"); + final Integer toPeriod = JdbcSupport.getInteger(rs, "ircdToPeriod"); + final Integer periodTypeId = JdbcSupport.getInteger(rs, "ircdPeriodTypeId"); + final EnumOptionData periodType = InterestRateChartEnumerations.periodType(periodTypeId); + final BigDecimal amountRangeFrom = rs.getBigDecimal("ircdAmountRangeFrom"); + final BigDecimal amountRangeTo = rs.getBigDecimal("ircdAmountRangeTo"); + final BigDecimal annualInterestRate = rs.getBigDecimal("ircdAnnualInterestRate"); + + // currency Slabs + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + // currency + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, + currencyDisplaySymbol, currencyNameCode); + + return InterestRateChartSlabData.instance(id, description, periodType, fromPeriod, toPeriod, amountRangeFrom, amountRangeTo, + annualInterestRate, currency); + } + + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartSlabWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartSlabWritePlatformService.java new file mode 100644 index 000000000..0f6c2fece --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartSlabWritePlatformService.java @@ -0,0 +1,18 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.service; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; + +public interface InterestRateChartSlabWritePlatformService { + + CommandProcessingResult create(JsonCommand command); + + CommandProcessingResult update(Long chartSlabId, Long interestChartId, JsonCommand command); + + CommandProcessingResult deleteChartSlab(Long chartSlabId, Long interestRateChartId); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartSlabWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartSlabWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 000000000..9048a14fe --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartSlabWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,105 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.service; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartRepositoryWrapper; +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartSlabDataValidator; +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartSlabRepository; +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChartSlab; +import org.mifosplatform.portfolio.savings.domain.SavingsProductRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class InterestRateChartSlabWritePlatformServiceJpaRepositoryImpl implements InterestRateChartSlabWritePlatformService { + + @SuppressWarnings("unused") + private final static Logger logger = LoggerFactory.getLogger(InterestRateChartSlabWritePlatformServiceJpaRepositoryImpl.class); + @SuppressWarnings("unused") + private final PlatformSecurityContext context; + private final InterestRateChartSlabDataValidator interestRateChartSlabDataValidator; + @SuppressWarnings("unused") + private final InterestRateChartAssembler interestRateChartAssembler; + private final InterestRateChartSlabAssembler interestRateChartSlabAssembler; + @SuppressWarnings("unused") + private final InterestRateChartRepositoryWrapper interestRateChartRepository; + private final InterestRateChartSlabRepository chartSlabRepository; + @SuppressWarnings("unused") + private final SavingsProductRepository savingsProductRepository; + + @Autowired + public InterestRateChartSlabWritePlatformServiceJpaRepositoryImpl(PlatformSecurityContext context, + final InterestRateChartSlabDataValidator interestRateChartSlabDataValidator, + final InterestRateChartAssembler interestRateChartAssembler, + final InterestRateChartRepositoryWrapper interestRateChartRepository, final SavingsProductRepository savingsProductRepository, + final InterestRateChartSlabRepository chartSlabRepository, + final InterestRateChartSlabAssembler interestRateChartSlabAssembler) { + this.context = context; + this.interestRateChartSlabDataValidator = interestRateChartSlabDataValidator; + this.interestRateChartAssembler = interestRateChartAssembler; + this.interestRateChartRepository = interestRateChartRepository; + this.savingsProductRepository = savingsProductRepository; + this.chartSlabRepository = chartSlabRepository; + this.interestRateChartSlabAssembler = interestRateChartSlabAssembler; + } + + @Override + @Transactional + public CommandProcessingResult create(JsonCommand command) { + this.interestRateChartSlabDataValidator.validateCreate(command.json()); + + final InterestRateChartSlab interestRateChartSlab = this.interestRateChartSlabAssembler.assembleFrom(command); + + this.chartSlabRepository.saveAndFlush(interestRateChartSlab); + + final Long interestRateChartId = interestRateChartSlab.getId(); + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withEntityId(interestRateChartId) // + .build(); + } + + @Override + @Transactional + public CommandProcessingResult update(Long chartSlabId, Long interestRateChartId, JsonCommand command) { + this.interestRateChartSlabDataValidator.validateUpdate(command.json()); + final Map changes = new LinkedHashMap(20); + final InterestRateChartSlab updateChartSlabs = this.interestRateChartSlabAssembler.assembleFrom(chartSlabId, + interestRateChartId); + + updateChartSlabs.update(command, changes); + + this.chartSlabRepository.saveAndFlush(updateChartSlabs); + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withEntityId(interestRateChartId) // + .with(changes).build(); + } + + @Override + @Transactional + public CommandProcessingResult deleteChartSlab(Long chartSlabId, Long interestRateChartId) { + final InterestRateChartSlab deleteChartSlabs = this.interestRateChartSlabAssembler.assembleFrom(chartSlabId, + interestRateChartId); + this.chartSlabRepository.delete(deleteChartSlabs); + return new CommandProcessingResultBuilder() // + .withEntityId(chartSlabId) // + .build(); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartWritePlatformService.java new file mode 100644 index 000000000..5bfe407ac --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartWritePlatformService.java @@ -0,0 +1,18 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.service; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; + +public interface InterestRateChartWritePlatformService { + + CommandProcessingResult create(JsonCommand command); + + CommandProcessingResult update(Long interestChartId, JsonCommand command); + + CommandProcessingResult deleteChart(Long interestChartId); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 000000000..22f70bbab --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/interestratechart/service/InterestRateChartWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,92 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.interestratechart.service; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartDataValidator; +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartRepositoryWrapper; +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChart; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class InterestRateChartWritePlatformServiceJpaRepositoryImpl implements InterestRateChartWritePlatformService { + + @SuppressWarnings("unused") + private final static Logger logger = LoggerFactory.getLogger(InterestRateChartWritePlatformServiceJpaRepositoryImpl.class); + @SuppressWarnings("unused") + private final PlatformSecurityContext context; + private final InterestRateChartDataValidator interestRateChartDataValidator; + private final InterestRateChartAssembler interestRateChartAssembler; + private final InterestRateChartRepositoryWrapper interestRateChartRepository; + + @Autowired + public InterestRateChartWritePlatformServiceJpaRepositoryImpl(PlatformSecurityContext context, + final InterestRateChartDataValidator interestRateChartDataValidator, + final InterestRateChartAssembler interestRateChartAssembler, + final InterestRateChartRepositoryWrapper interestRateChartRepository) { + this.context = context; + this.interestRateChartDataValidator = interestRateChartDataValidator; + this.interestRateChartAssembler = interestRateChartAssembler; + this.interestRateChartRepository = interestRateChartRepository; + } + + @Override + @Transactional + public CommandProcessingResult create(JsonCommand command) { + this.interestRateChartDataValidator.validateForCreate(command.json()); + + final InterestRateChart interestRateChart = this.interestRateChartAssembler.assembleFrom(command); + + this.interestRateChartRepository.saveAndFlush(interestRateChart); + + final Long interestRateChartId = interestRateChart.getId(); + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withEntityId(interestRateChartId) // + .build(); + } + + @Override + @Transactional + public CommandProcessingResult update(Long interestRateChartId, JsonCommand command) { + this.interestRateChartDataValidator.validateUpdate(command.json()); + final Map changes = new LinkedHashMap(20); + final InterestRateChart interestRateChart = this.interestRateChartAssembler.assembleFrom(interestRateChartId); + + interestRateChart.update(command, changes); + + this.interestRateChartRepository.saveAndFlush(interestRateChart); + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withEntityId(interestRateChartId) // + .with(changes).build(); + } + + @Override + @Transactional + public CommandProcessingResult deleteChart(Long chartId) { + final InterestRateChart chart = this.interestRateChartRepository.findOneWithNotFoundDetection(chartId); + // validate if chart is associated with any accounts + + this.interestRateChartRepository.delete(chart); + return new CommandProcessingResultBuilder() // + .withEntityId(chartId) // + .build(); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index d12a8a3a3..65933448d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -1748,7 +1748,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final JsonElement parsedCommand = this.fromApiJsonHelper.parse(overdueInstallment.toString()); final JsonCommand command = JsonCommand.from(overdueInstallment.toString(), parsedCommand, this.fromApiJsonHelper, - null, null, null, null, null, overdueInstallment.getLoanId(), null, null, null, null); + null, null, null, null, null, overdueInstallment.getLoanId(), null, null, null, null, null, null); applyChargeToOverdueLoanInstallment(overdueInstallment.getLoanId(), overdueInstallment.getChargeId(), overdueInstallment.getPeriodNumber(), command); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/DepositAccountOnClosureType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/DepositAccountOnClosureType.java new file mode 100644 index 000000000..14ee6f92c --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/DepositAccountOnClosureType.java @@ -0,0 +1,86 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings; + +import java.util.ArrayList; +import java.util.List; + +import org.mifosplatform.portfolio.savings.domain.FixedDepositAccount; +import org.mifosplatform.portfolio.savings.domain.RecurringDepositAccount; + +/** + * An enumeration of different options available on account closure + * {@link FixedDepositAccount} & {@link RecurringDepositAccount}. + */ +public enum DepositAccountOnClosureType { + + INVALID(0, "depositAccountClosureType.invalid"), // + WITHDRAW_DEPOSIT(100, "depositAccountClosureType.withdrawDeposit"), // + TRANSFER_TO_SAVINGS(200, "depositAccountClosureType.transferToSavings"), // + REINVEST(300, "depositAccountClosureType.reinvest"); // + + private final Integer value; + private final String code; + + private DepositAccountOnClosureType(final Integer value, final String code) { + this.value = value; + this.code = code; + } + + public Integer getValue() { + return this.value; + } + + public String getCode() { + return this.code; + } + + public static DepositAccountOnClosureType fromInt(final Integer closureTypeValue) { + + if (closureTypeValue == null) { return DepositAccountOnClosureType.INVALID; } + + DepositAccountOnClosureType accountOnClosureType = DepositAccountOnClosureType.INVALID; + switch (closureTypeValue) { + case 100: + accountOnClosureType = DepositAccountOnClosureType.WITHDRAW_DEPOSIT; + break; + case 200: + accountOnClosureType = DepositAccountOnClosureType.TRANSFER_TO_SAVINGS; + break; + case 300: + accountOnClosureType = DepositAccountOnClosureType.REINVEST; + break; + } + return accountOnClosureType; + } + + public boolean isWithdarwDeposit() { + return this.value.equals(DepositAccountOnClosureType.WITHDRAW_DEPOSIT.getValue()); + } + + public boolean isTransferToSavings() { + return this.value.equals(DepositAccountOnClosureType.TRANSFER_TO_SAVINGS.getValue()); + } + + public boolean isReinvest() { + return this.value.equals(DepositAccountOnClosureType.REINVEST.getValue()); + } + + public boolean isInvalid() { + return this.value.equals(DepositAccountOnClosureType.INVALID.getValue()); + } + + public static Object[] integerValues() { + final List values = new ArrayList(); + for (final DepositAccountOnClosureType enumType : values()) { + if (!enumType.isInvalid()) { + values.add(enumType.getValue()); + } + } + + return values.toArray(); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/DepositAccountType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/DepositAccountType.java new file mode 100644 index 000000000..bd14b112e --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/DepositAccountType.java @@ -0,0 +1,108 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings; + +import org.apache.commons.lang.StringUtils; +import org.mifosplatform.portfolio.savings.domain.SavingsAccount; + +/** + * An enumeration of different transactions that can occur on a + * {@link SavingsAccount}. + */ +public enum DepositAccountType { + + INVALID(0, "depositAccountType.invalid"), // + SAVINGS_DEPOSIT(100, "depositAccountType.savingsDeposit"), // + FIXED_DEPOSIT(200, "depositAccountType.fixedDeposit"), // + RECURRING_DEPOSIT(300, "depositAccountType.recurringDeposit"), // + CURRENT_DEPOSIT(400, "depositAccountType.currentDeposit"); + + private final Integer value; + private final String code; + private static final String SPACE = " "; + + private DepositAccountType(final Integer value, final String code) { + this.value = value; + this.code = code; + } + + public Integer getValue() { + return this.value; + } + + public String getCode() { + return this.code; + } + + public static DepositAccountType fromInt(final Integer transactionType) { + + if (transactionType == null) { return DepositAccountType.INVALID; } + + DepositAccountType depositAccountType = DepositAccountType.INVALID; + switch (transactionType) { + case 100: + depositAccountType = DepositAccountType.SAVINGS_DEPOSIT; + break; + case 200: + depositAccountType = DepositAccountType.FIXED_DEPOSIT; + break; + case 300: + depositAccountType = DepositAccountType.RECURRING_DEPOSIT; + break; + case 400: + depositAccountType = DepositAccountType.CURRENT_DEPOSIT; + break; + } + return depositAccountType; + } + + public boolean isSavingsDeposit() { + return this.value.equals(DepositAccountType.SAVINGS_DEPOSIT.getValue()); + } + + public boolean isFixedDeposit() { + return this.value.equals(DepositAccountType.FIXED_DEPOSIT.getValue()); + } + + public boolean isRecurringDeposit() { + return this.value.equals(DepositAccountType.RECURRING_DEPOSIT.getValue()); + } + + public boolean isCurrentDeposit() { + return this.value.equals(DepositAccountType.CURRENT_DEPOSIT.getValue()); + } + + @Override + public String toString() { + return StringUtils.replace(code, "_", SPACE); + } + + public String resourceName() { + + String resourceName = "INVALID"; + + switch (this) { + case FIXED_DEPOSIT: + resourceName = DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME; + break; + case RECURRING_DEPOSIT: + resourceName = DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME; + break; + case SAVINGS_DEPOSIT: + resourceName = DepositsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME; + break; + default: + resourceName = "INVALID"; + break; + } + + return resourceName; + } + + public boolean isInvalid() { + return this.value.equals(DepositAccountType.INVALID.value); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/DepositsApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/DepositsApiConstants.java new file mode 100644 index 000000000..0ea7ae729 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/DepositsApiConstants.java @@ -0,0 +1,357 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.mifosplatform.accounting.common.AccountingConstants.SAVINGS_PRODUCT_ACCOUNTING_PARAMS; + +public class DepositsApiConstants { + + // Deposit products + public static final String FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME = "fixeddeposit"; + public static final String RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME = "recurringdeposit"; + + // Deposti accounts + public static final String FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME = "fixeddepositaccount"; + public static final String RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME = "recurringdepositaccount"; + + public static final String SAVINGS_ACCOUNT_RESOURCE_NAME = "savingsaccount"; + public static final String SAVINGS_ACCOUNT_TRANSACTION_RESOURCE_NAME = "savingsaccount.transaction"; + public static final String SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME = "savingsaccountcharge"; + + // deposit product actions + public static String summitalAction = ".summital"; + public static String approvalAction = ".approval"; + public static String undoApprovalAction = ".undoApproval"; + public static String rejectAction = ".reject"; + public static String withdrawnByApplicantAction = ".withdrawnByApplicant"; + public static String activateAction = ".activate"; + public static String modifyApplicationAction = ".modify"; + public static String deleteApplicationAction = ".delete"; + public static String undoTransactionAction = ".undotransaction"; + public static String applyAnnualFeeTransactionAction = ".applyannualfee"; + public static String adjustTransactionAction = ".adjusttransaction"; + public static String closeAction = ".close"; + public static String preMatureCloseAction = ".preMatureClose"; + public static String payChargeTransactionAction = ".paycharge"; + public static String waiveChargeTransactionAction = ".waivecharge"; + + // command + public static String COMMAND_UNDO_TRANSACTION = "undo"; + public static String COMMAND_ADJUST_TRANSACTION = "modify"; + public static String COMMAND_WAIVE_CHARGE = "waive"; + public static String COMMAND_PAY_CHARGE = "paycharge"; + + // general + public static final String localeParamName = "locale"; + public static final String dateFormatParamName = "dateFormat"; + public static final String monthDayFormatParamName = "monthDayFormat"; + + // deposit product and account parameters + public static final String idParamName = "id"; + public static final String accountNoParamName = "accountNo"; + public static final String externalIdParamName = "externalId"; + public static final String statusParamName = "status"; + public static final String clientIdParamName = "clientId"; + public static final String groupIdParamName = "groupId"; + public static final String productIdParamName = "productId"; + public static final String fieldOfficerIdParamName = "fieldOfficerId"; + + public static final String submittedOnDateParamName = "submittedOnDate"; + public static final String rejectedOnDateParamName = "rejectedOnDate"; + public static final String withdrawnOnDateParamName = "withdrawnOnDate"; + public static final String approvedOnDateParamName = "approvedOnDate"; + public static final String activatedOnDateParamName = "activatedOnDate"; + public static final String closedOnDateParamName = "closedOnDate"; + + public static final String activeParamName = "active"; + public static final String nameParamName = "name"; + public static final String shortNameParamName = "shortName"; + public static final String descriptionParamName = "description"; + public static final String currencyCodeParamName = "currencyCode"; + public static final String digitsAfterDecimalParamName = "digitsAfterDecimal"; + public static final String inMultiplesOfParamName = "inMultiplesOf"; + public static final String nominalAnnualInterestRateParamName = "nominalAnnualInterestRate"; + public static final String interestCompoundingPeriodTypeParamName = "interestCompoundingPeriodType"; + public static final String interestPostingPeriodTypeParamName = "interestPostingPeriodType"; + public static final String interestCalculationTypeParamName = "interestCalculationType"; + public static final String interestCalculationDaysInYearTypeParamName = "interestCalculationDaysInYearType"; + public static final String lockinPeriodFrequencyParamName = "lockinPeriodFrequency"; + public static final String lockinPeriodFrequencyTypeParamName = "lockinPeriodFrequencyType"; + public static final String feeAmountParamName = "feeAmount";// to be deleted + public static final String feeOnMonthDayParamName = "feeOnMonthDay"; + public static final String feeIntervalParamName = "feeInterval"; + public static final String accountingRuleParamName = "accountingRule"; + public static final String paymentTypeIdParamName = "paymentTypeId"; + public static final String transactionAccountNumberParamName = "accountNumber"; + public static final String checkNumberParamName = "checkNumber"; + public static final String routingCodeParamName = "routingCode"; + public static final String receiptNumberParamName = "receiptNumber"; + public static final String bankNumberParamName = "bankNumber"; + + // Preclosure parameters + public static final String interestFreePeriodApplicableParamName = "interestFreePeriodApplicable"; + public static final String interestFreeFromPeriodParamName = "interestFreeFromPeriod"; + public static final String interestFreeToPeriodParamName = "interestFreeToPeriod"; + public static final String interestFreePeriodFrequencyTypeIdParamName = "interestFreePeriodFrequencyTypeId"; + public static final String preClosurePenalApplicableParamName = "preClosurePenalApplicable"; + public static final String preClosurePenalInterestParamName = "preClosurePenalInterest"; + public static final String preClosurePenalInterestOnTypeIdParamName = "preClosurePenalInterestOnTypeId"; + public static final String interestFreePeriodFrequencyType = "interestFreePeriodFrequencyType"; + public static final String preClosurePenalInterestOnType = "preClosurePenalInterestOnType"; + + // term paramters + public static final String minDepositTermParamName = "minDepositTerm"; + public static final String maxDepositTermParamName = "maxDepositTerm"; + public static final String minDepositTermTypeIdParamName = "minDepositTermTypeId"; + public static final String maxDepositTermTypeIdParamName = "maxDepositTermTypeId"; + public static final String minDepositTermType = "minDepositTermType"; + public static final String maxDepositTermType = "maxDepositTermType"; + public static final String inMultiplesOfDepositTermParamName = "inMultiplesOfDepositTerm"; + public static final String inMultiplesOfDepositTermTypeIdParamName = "inMultiplesOfDepositTermTypeId"; + public static final String inMultiplesOfDepositTermType = "inMultiplesOfDepositTermType"; + + public static final String depositAmountParamName = "depositAmount"; + public static final String depositPeriodParamName = "depositPeriod"; + public static final String depositPeriodFrequencyIdParamName = "depositPeriodFrequencyId"; + + // recurring parameters + public static final String recurringDepositAmountParamName = "recurringDepositAmount"; + public static final String recurringDepositType = "recurringDepositType"; + public static final String recurringDepositFrequencyType = "recurringDepositFrequencyType"; + public static final String recurringDepositFrequencyParamName = "recurringDepositFrequency"; + public static final String recurringDepositTypeIdParamName = "recurringDepositTypeId"; + public static final String recurringDepositFrequencyTypeIdParamName = "recurringDepositFrequencyTypeId"; + + // transaction parameters + public static final String transactionDateParamName = "transactionDate"; + public static final String transactionAmountParamName = "transactionAmount"; + public static final String paymentDetailDataParamName = "paymentDetailData"; + public static final String runningBalanceParamName = "runningBalance"; + public static final String reversedParamName = "reversed"; + public static final String dateParamName = "date"; + + // charges parameters + public static final String chargeIdParamName = "chargeId"; + public static final String chargesParamName = "charges"; + public static final String savingsAccountChargeIdParamName = "savingsAccountChargeId"; + public static final String chargeNameParamName = "name"; + public static final String penaltyParamName = "penalty"; + public static final String chargeTimeTypeParamName = "chargeTimeType"; + public static final String dueAsOfDateParamName = "dueDate"; + public static final String chargeCalculationTypeParamName = "chargeCalculationType"; + public static final String percentageParamName = "percentage"; + public static final String amountPercentageAppliedToParamName = "amountPercentageAppliedTo"; + public static final String currencyParamName = "currency"; + public static final String amountWaivedParamName = "amountWaived"; + public static final String amountWrittenOffParamName = "amountWrittenOff"; + public static final String amountOutstandingParamName = "amountOutstanding"; + public static final String amountOrPercentageParamName = "amountOrPercentage"; + public static final String amountParamName = "amount"; + public static final String amountPaidParamName = "amountPaid"; + public static final String chargeOptionsParamName = "chargeOptions"; + public static final String chargePaymentModeParamName = "chargePaymentMode"; + + public static final String noteParamName = "note"; + public static final String chartsParamName = "charts"; + public static final String chartIdParamName = "chartId"; + + // deposit account associations + public static final String transactions = "transactions"; + public static final String charges = "charges"; + public static final String activeChart = "activeChart"; + + // account closure + public static final String onAccountClosureIdParamName = "onAccountClosureId"; + public static final String transferDescriptionParamName = "transferDescription"; + public static final String toSavingsAccountIdParamName = "toSavingsAccountId"; + public static final String savingsAccounts = "savingsAccounts"; + + public static final String preMatureCloseOnDateParamName = "preMatureCloseOnDate"; + + // template + public static final String chartTemplate = "chartTemplate"; + + // allowed column names for sorting the query result + public final static Set supportedOrderByValues = new HashSet(Arrays.asList("id", "accountNumbr", "officeId", + "officeName")); + + /** + * Deposit Product Parameters + */ + private static final Set DEPOSIT_PRODUCT_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, + monthDayFormatParamName, nameParamName, shortNameParamName, descriptionParamName, currencyCodeParamName, + digitsAfterDecimalParamName, inMultiplesOfParamName, nominalAnnualInterestRateParamName, + interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName, + interestCalculationDaysInYearTypeParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, + accountingRuleParamName, chargesParamName, SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_SAVINGS.getValue(), + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(), + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_CONTROL.getValue(), SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_REFERENCE.getValue(), + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.FEE_INCOME_ACCOUNT_MAPPING.getValue(), + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.PENALTY_INCOME_ACCOUNT_MAPPING.getValue(), chartsParamName)); + + private static final Set PRECLOSURE_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList( + interestFreePeriodApplicableParamName, interestFreeFromPeriodParamName, interestFreeToPeriodParamName, + interestFreePeriodFrequencyTypeIdParamName, preClosurePenalApplicableParamName, preClosurePenalInterestParamName, + preClosurePenalInterestOnTypeIdParamName)); + + private static final Set PRECLOSURE_RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList( + interestFreePeriodApplicableParamName, interestFreeFromPeriodParamName, interestFreeToPeriodParamName, + interestFreePeriodFrequencyType, preClosurePenalApplicableParamName, preClosurePenalInterestParamName, + preClosurePenalInterestOnType)); + + private static final Set DEPOSIT_TERM_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(minDepositTermParamName, + maxDepositTermParamName, minDepositTermTypeIdParamName, maxDepositTermTypeIdParamName, inMultiplesOfDepositTermParamName, + inMultiplesOfDepositTermTypeIdParamName)); + + private static final Set DEPOSIT_TERM_RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList(minDepositTermParamName, + maxDepositTermParamName, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTermParamName, + inMultiplesOfDepositTermType)); + + private static final Set RECURRING_DETAILS_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList( + recurringDepositAmountParamName, recurringDepositTypeIdParamName, recurringDepositFrequencyTypeIdParamName, + recurringDepositFrequencyParamName)); + + private static final Set RECURRING_DETAILS_RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList( + recurringDepositAmountParamName, recurringDepositType, recurringDepositFrequencyParamName, recurringDepositFrequencyType)); + + public static final Set DEPOSIT_PRECLOSURE_CALCULATION_REQUEST_DATA_PARAMETERS = new HashSet( + Arrays.asList(preMatureCloseOnDateParamName)); + + public static final Set FIXED_DEPOSIT_PRODUCT_REQUEST_DATA_PARAMETERS = fixedDepositProductRequestData(); + public static final Set FIXED_DEPOSIT_PRODUCT_RESPONSE_DATA_PARAMETERS = fixedDepositProductResponseData(); + + public static final Set RECURRING_DEPOSIT_PRODUCT_REQUEST_DATA_PARAMETERS = recurringDepositProductRequestData(); + public static final Set RECURRING_DEPOSIT_PRODUCT_RESPONSE_DATA_PARAMETERS = recurringDepositProductResponseData(); + + private static Set fixedDepositProductRequestData() { + final Set fixedDepositRequestData = new HashSet(); + fixedDepositRequestData.addAll(DEPOSIT_PRODUCT_REQUEST_DATA_PARAMETERS); + fixedDepositRequestData.addAll(PRECLOSURE_REQUEST_DATA_PARAMETERS); + fixedDepositRequestData.addAll(DEPOSIT_TERM_REQUEST_DATA_PARAMETERS); + return fixedDepositRequestData; + } + + private static Set fixedDepositProductResponseData() { + final Set fixedDepositRequestData = new HashSet(); + fixedDepositRequestData.addAll(DEPOSIT_PRODUCT_REQUEST_DATA_PARAMETERS); + fixedDepositRequestData.addAll(PRECLOSURE_RESPONSE_DATA_PARAMETERS); + fixedDepositRequestData.addAll(DEPOSIT_TERM_RESPONSE_DATA_PARAMETERS); + return fixedDepositRequestData; + } + + private static Set recurringDepositProductRequestData() { + final Set recurringDepositRequestData = new HashSet(); + recurringDepositRequestData.addAll(DEPOSIT_PRODUCT_REQUEST_DATA_PARAMETERS); + recurringDepositRequestData.addAll(PRECLOSURE_REQUEST_DATA_PARAMETERS); + recurringDepositRequestData.addAll(DEPOSIT_TERM_REQUEST_DATA_PARAMETERS); + recurringDepositRequestData.addAll(RECURRING_DETAILS_REQUEST_DATA_PARAMETERS); + return recurringDepositRequestData; + } + + private static Set recurringDepositProductResponseData() { + final Set recurringDepositRequestData = new HashSet(); + recurringDepositRequestData.addAll(DEPOSIT_PRODUCT_REQUEST_DATA_PARAMETERS); + recurringDepositRequestData.addAll(PRECLOSURE_RESPONSE_DATA_PARAMETERS); + recurringDepositRequestData.addAll(DEPOSIT_TERM_RESPONSE_DATA_PARAMETERS); + recurringDepositRequestData.addAll(RECURRING_DETAILS_RESPONSE_DATA_PARAMETERS); + return recurringDepositRequestData; + } + + /** + * Depost Account parameters + */ + + public static final Set DEPOSIT_ACCOUNT_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, + dateFormatParamName, monthDayFormatParamName, accountNoParamName, externalIdParamName, clientIdParamName, groupIdParamName, + productIdParamName, fieldOfficerIdParamName, submittedOnDateParamName, nominalAnnualInterestRateParamName, + interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName, + interestCalculationDaysInYearTypeParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, + chargesParamName, chartsParamName, depositAmountParamName, depositPeriodParamName, depositPeriodFrequencyIdParamName, + savingsAccounts)); + + public static final Set FIXED_DEPOSIT_ACCOUNT_REQUEST_DATA_PARAMETERS = fixedDepositAccountRequestData(); + public static final Set FIXED_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS = fixedDepositAccountResponseData(); + + public static final Set RECURRING_DEPOSIT_ACCOUNT_REQUEST_DATA_PARAMETERS = recurringDepositAccountRequestData(); + public static final Set RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS = recurringDepositAccountResponseData(); + + private static Set fixedDepositAccountRequestData() { + final Set fixedDepositRequestData = new HashSet(); + fixedDepositRequestData.addAll(DEPOSIT_ACCOUNT_REQUEST_DATA_PARAMETERS); + fixedDepositRequestData.addAll(PRECLOSURE_REQUEST_DATA_PARAMETERS); + fixedDepositRequestData.addAll(DEPOSIT_TERM_REQUEST_DATA_PARAMETERS); + return fixedDepositRequestData; + } + + private static Set fixedDepositAccountResponseData() { + final Set fixedDepositResponseData = new HashSet(); + fixedDepositResponseData.addAll(DEPOSIT_ACCOUNT_REQUEST_DATA_PARAMETERS); + fixedDepositResponseData.addAll(PRECLOSURE_RESPONSE_DATA_PARAMETERS); + fixedDepositResponseData.addAll(DEPOSIT_TERM_RESPONSE_DATA_PARAMETERS); + return fixedDepositResponseData; + } + + private static Set recurringDepositAccountRequestData() { + final Set recurringDepositRequestData = new HashSet(); + recurringDepositRequestData.addAll(DEPOSIT_ACCOUNT_REQUEST_DATA_PARAMETERS); + recurringDepositRequestData.addAll(PRECLOSURE_REQUEST_DATA_PARAMETERS); + recurringDepositRequestData.addAll(DEPOSIT_TERM_REQUEST_DATA_PARAMETERS); + recurringDepositRequestData.addAll(RECURRING_DETAILS_REQUEST_DATA_PARAMETERS); + return recurringDepositRequestData; + } + + private static Set recurringDepositAccountResponseData() { + final Set recurringDepositResponseData = new HashSet(); + recurringDepositResponseData.addAll(DEPOSIT_ACCOUNT_REQUEST_DATA_PARAMETERS); + recurringDepositResponseData.addAll(PRECLOSURE_RESPONSE_DATA_PARAMETERS); + recurringDepositResponseData.addAll(DEPOSIT_TERM_RESPONSE_DATA_PARAMETERS); + recurringDepositResponseData.addAll(RECURRING_DETAILS_RESPONSE_DATA_PARAMETERS); + return recurringDepositResponseData; + } + + public static final Set DEPOSIT_ACCOUNT_TRANSACTION_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList( + localeParamName, dateFormatParamName, transactionDateParamName, transactionAmountParamName, paymentTypeIdParamName, + transactionAccountNumberParamName, checkNumberParamName, routingCodeParamName, receiptNumberParamName, bankNumberParamName)); + + public static final Set FIXED_DEPOSIT_TRANSACTION_RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList(idParamName, + "accountId", accountNoParamName, "currency", "amount", dateParamName, paymentDetailDataParamName, runningBalanceParamName, + reversedParamName)); + + public static final Set RECURRING_DEPOSIT_TRANSACTION_RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList(idParamName, + "accountId", accountNoParamName, "currency", "amount", dateParamName, paymentDetailDataParamName, runningBalanceParamName, + reversedParamName)); + + public static final Set SAVINGS_ACCOUNT_ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, + dateFormatParamName, activatedOnDateParamName)); + + public static final Set DEPOSIT_ACCOUNT_CLOSE_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, + dateFormatParamName, closedOnDateParamName, noteParamName, onAccountClosureIdParamName, paymentTypeIdParamName, + transactionAccountNumberParamName, checkNumberParamName, routingCodeParamName, receiptNumberParamName, bankNumberParamName, + transferDescriptionParamName, toSavingsAccountIdParamName)); + + public static final Set DEPOSIT_ACCOUNT_PRE_MATURE_CALCULATION_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList( + localeParamName, dateFormatParamName, closedOnDateParamName)); + + public static final Set SAVINGS_ACCOUNT_CHARGES_RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList(chargeIdParamName, + savingsAccountChargeIdParamName, chargeNameParamName, penaltyParamName, chargeTimeTypeParamName, dueAsOfDateParamName, + chargeCalculationTypeParamName, percentageParamName, amountPercentageAppliedToParamName, currencyParamName, + amountWaivedParamName, amountWrittenOffParamName, amountOutstandingParamName, amountOrPercentageParamName, amountParamName, + amountPaidParamName, chargeOptionsParamName)); + + public static final Set SAVINGS_ACCOUNT_CHARGES_ADD_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList( + chargeIdParamName, amountParamName, dueAsOfDateParamName, dateFormatParamName, localeParamName, feeOnMonthDayParamName, + monthDayFormatParamName, feeIntervalParamName)); + + public static final Set SAVINGS_ACCOUNT_CHARGES_PAY_CHARGE_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList( + amountParamName, dueAsOfDateParamName, dateFormatParamName, localeParamName)); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/PreClosurePenalInterestOnType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/PreClosurePenalInterestOnType.java new file mode 100644 index 000000000..476f6dcfd --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/PreClosurePenalInterestOnType.java @@ -0,0 +1,74 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings; + +import java.util.ArrayList; +import java.util.List; + + +/** + * An enumeration of supported calendar periods used in savings. + */ +public enum PreClosurePenalInterestOnType { + INVALID(0, "preClosurePenalInterestOnType.invalid"), + WHOLE_TERM(1, "preClosurePenalInterestOnType.wholeTerm"), // + TILL_PREMATURE_WITHDRAWAL(2, "preClosurePenalInterestOnType.tillPrematureWithdrawal"); // + + + private final Integer value; + private final String code; + + private PreClosurePenalInterestOnType(final Integer value, final String code) { + this.value = value; + this.code = code; + } + + public Integer getValue() { + return this.value; + } + + public String getCode() { + return this.code; + } + + public static PreClosurePenalInterestOnType fromInt(final Integer type) { + PreClosurePenalInterestOnType penalInterestType = PreClosurePenalInterestOnType.INVALID; + if (type != null) { + switch (type) { + case 1: + penalInterestType = PreClosurePenalInterestOnType.WHOLE_TERM; + break; + case 2: + penalInterestType = PreClosurePenalInterestOnType.TILL_PREMATURE_WITHDRAWAL; + break; + } + } + return penalInterestType; + } + + public static Object[] integerValues() { + final List values = new ArrayList(); + for (final PreClosurePenalInterestOnType enumType : values()) { + if (enumType.getValue() > 0) { + values.add(enumType.getValue()); + } + } + + return values.toArray(); + } + + public boolean isInvalid(){ + return this.value.equals(PreClosurePenalInterestOnType.INVALID.value); + } + + public boolean isWholeTerm() { + return this.value.equals(PreClosurePenalInterestOnType.WHOLE_TERM.getValue()); + } + + public boolean isTillPrematureWithdrawal() { + return this.value.equals(PreClosurePenalInterestOnType.TILL_PREMATURE_WITHDRAWAL.getValue()); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/RecurringDepositType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/RecurringDepositType.java new file mode 100644 index 000000000..f4be109a7 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/RecurringDepositType.java @@ -0,0 +1,66 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings; + +import java.util.ArrayList; +import java.util.List; + + +/** + * An enumeration of supported calendar periods used in savings. + */ +public enum RecurringDepositType { + INVALID(0, "recurringDepositType.invalid"), + VOLUNTARY(1, "recurringDepositType.voluntary"), // + MANDATORY(2, "recurringDepositType.mandatory"); // + + + private final Integer value; + private final String code; + + private RecurringDepositType(final Integer value, final String code) { + this.value = value; + this.code = code; + } + + public Integer getValue() { + return this.value; + } + + public String getCode() { + return this.code; + } + + public static RecurringDepositType fromInt(final Integer type) { + RecurringDepositType rdType = RecurringDepositType.INVALID; + if (type != null) { + switch (type) { + case 1: + rdType = RecurringDepositType.VOLUNTARY; + break; + case 2: + rdType = RecurringDepositType.MANDATORY; + break; + } + } + return rdType; + } + + public static Object[] integerValues() { + final List values = new ArrayList(); + for (final RecurringDepositType enumType : values()) { + if (enumType.getValue() > 0) { + values.add(enumType.getValue()); + } + } + + return values.toArray(); + } + + public boolean isInvalid(){ + return this.value.equals(RecurringDepositType.INVALID.value); + } +} 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 032324f50..a50afe4dc 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 @@ -35,6 +35,7 @@ public class SavingsApiConstants { public static String closeAction = ".close"; public static String payChargeTransactionAction = ".paycharge"; public static String waiveChargeTransactionAction = ".waivecharge"; + public static String updateMaturityDetailsAction = ".updateMaturityDetails"; // command public static String COMMAND_UNDO_TRANSACTION = "undo"; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsPeriodFrequencyType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsPeriodFrequencyType.java index 7323071a0..61b6a5be2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsPeriodFrequencyType.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsPeriodFrequencyType.java @@ -5,6 +5,9 @@ */ package org.mifosplatform.portfolio.savings; +import java.util.ArrayList; +import java.util.List; + /** * An enumeration of supported calendar periods used in savings. */ @@ -51,4 +54,19 @@ public enum SavingsPeriodFrequencyType { } return repaymentFrequencyType; } + + public boolean isInvalid() { + return this.value.equals(SavingsPeriodFrequencyType.INVALID.value); + } + + public static Object[] integerValues() { + final List values = new ArrayList(); + for (final SavingsPeriodFrequencyType enumType : values()) { + if (!enumType.isInvalid()) { + values.add(enumType.getValue()); + } + } + + return values.toArray(); + } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/FixedDepositAccountTransactionsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/FixedDepositAccountTransactionsApiResource.java new file mode 100644 index 000000000..5409d7c60 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/FixedDepositAccountTransactionsApiResource.java @@ -0,0 +1,173 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.api; + +import java.util.Collection; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; + +import org.apache.commons.lang.StringUtils; +import org.mifosplatform.commands.domain.CommandWrapper; +import org.mifosplatform.commands.service.CommandWrapperBuilder; +import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; +import org.mifosplatform.infrastructure.codes.data.CodeValueData; +import org.mifosplatform.infrastructure.codes.service.CodeValueReadPlatformService; +import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.exception.UnrecognizedQueryParamException; +import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.DepositsApiConstants; +import org.mifosplatform.portfolio.savings.SavingsApiConstants; +import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionData; +import org.mifosplatform.portfolio.savings.service.SavingsAccountReadPlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Path("/fixeddepositaccounts/{fixedDepositAccountId}/transactions") +@Component +@Scope("singleton") +public class FixedDepositAccountTransactionsApiResource { + + private final PlatformSecurityContext context; + private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final ApiRequestParameterHelper apiRequestParameterHelper; + private final SavingsAccountReadPlatformService savingsAccountReadPlatformService; + private final CodeValueReadPlatformService codeValueReadPlatformService; + + @Autowired + public FixedDepositAccountTransactionsApiResource(final PlatformSecurityContext context, + final DefaultToApiJsonSerializer toApiJsonSerializer, + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, + final ApiRequestParameterHelper apiRequestParameterHelper, + final SavingsAccountReadPlatformService savingsAccountReadPlatformService, + final CodeValueReadPlatformService codeValueReadPlatformService) { + this.context = context; + this.toApiJsonSerializer = toApiJsonSerializer; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + this.apiRequestParameterHelper = apiRequestParameterHelper; + this.savingsAccountReadPlatformService = savingsAccountReadPlatformService; + this.codeValueReadPlatformService = codeValueReadPlatformService; + } + + private boolean is(final String commandParam, final String commandValue) { + return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue); + } + + @GET + @Path("template") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveTemplate(@PathParam("fixedDepositAccountId") final Long fixedDepositAccountId, + // @QueryParam("command") final String commandParam, + @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME); + + SavingsAccountTransactionData savingsAccount = this.savingsAccountReadPlatformService.retrieveDepositTransactionTemplate( + fixedDepositAccountId, DepositAccountType.FIXED_DEPOSIT); + final Collection paymentTypeOptions = this.codeValueReadPlatformService + .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); + savingsAccount = SavingsAccountTransactionData.templateOnTop(savingsAccount, paymentTypeOptions); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, savingsAccount, + SavingsApiConstants.SAVINGS_TRANSACTION_RESPONSE_DATA_PARAMETERS); + } + + @GET + @Path("{transactionId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveOne(@PathParam("fixedDepositAccountId") final Long fixedDepositAccountId, + @PathParam("transactionId") final Long transactionId, @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME); + SavingsAccountTransactionData transactionData = this.savingsAccountReadPlatformService.retrieveSavingsTransaction( + fixedDepositAccountId, transactionId, DepositAccountType.FIXED_DEPOSIT); + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + if (settings.isTemplate()) { + final Collection paymentTypeOptions = this.codeValueReadPlatformService + .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); + transactionData = SavingsAccountTransactionData.templateOnTop(transactionData, paymentTypeOptions); + } + + return this.toApiJsonSerializer.serialize(settings, transactionData, + DepositsApiConstants.FIXED_DEPOSIT_TRANSACTION_RESPONSE_DATA_PARAMETERS); + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String transaction(@PathParam("fixedDepositAccountId") final Long fixedDepositAccountId, + @QueryParam("command") final String commandParam, final String apiRequestBodyAsJson) { + + final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson); + + CommandProcessingResult result = null; + + if (is(commandParam, "deposit")) { + final CommandWrapper commandRequest = builder.fixedDepositAccountDeposit(fixedDepositAccountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "withdrawal")) { + final CommandWrapper commandRequest = builder.fixedDepositAccountWithdrawal(fixedDepositAccountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } + + if (result == null) { + // + throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "deposit", "withdrawal" }); + } + + return this.toApiJsonSerializer.serialize(result); + } + + @POST + @Path("{transactionId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String adjustTransaction(@PathParam("fixedDepositAccountId") final Long fixedDepositAccountId, + @PathParam("transactionId") final Long transactionId, @QueryParam("command") final String commandParam, + final String apiRequestBodyAsJson) { + + String jsonApiRequest = apiRequestBodyAsJson; + if (StringUtils.isBlank(jsonApiRequest)) { + jsonApiRequest = "{}"; + } + + final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(jsonApiRequest); + + CommandProcessingResult result = null; + if (is(commandParam, DepositsApiConstants.COMMAND_UNDO_TRANSACTION)) { + final CommandWrapper commandRequest = builder.undoSavingsAccountTransaction(fixedDepositAccountId, transactionId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, DepositsApiConstants.COMMAND_ADJUST_TRANSACTION)) { + final CommandWrapper commandRequest = builder.adjustSavingsAccountTransaction(fixedDepositAccountId, transactionId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } + + if (result == null) { + // + throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "undo", "modify" }); + } + + return this.toApiJsonSerializer.serialize(result); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/FixedDepositAccountsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/FixedDepositAccountsApiResource.java new file mode 100644 index 000000000..98563eb18 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/FixedDepositAccountsApiResource.java @@ -0,0 +1,318 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.api; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; + +import org.apache.commons.lang.StringUtils; +import org.mifosplatform.commands.domain.CommandWrapper; +import org.mifosplatform.commands.service.CommandWrapperBuilder; +import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; +import org.mifosplatform.infrastructure.core.api.ApiParameterHelper; +import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; +import org.mifosplatform.infrastructure.core.api.JsonQuery; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.data.PaginationParameters; +import org.mifosplatform.infrastructure.core.exception.UnrecognizedQueryParamException; +import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.infrastructure.core.service.Page; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.DepositsApiConstants; +import org.mifosplatform.portfolio.savings.SavingsApiConstants; +import org.mifosplatform.portfolio.savings.data.DepositAccountData; +import org.mifosplatform.portfolio.savings.data.FixedDepositAccountData; +import org.mifosplatform.portfolio.savings.data.SavingsAccountChargeData; +import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionData; +import org.mifosplatform.portfolio.savings.service.DepositAccountPreMatureCalculationPlatformService; +import org.mifosplatform.portfolio.savings.service.DepositAccountReadPlatformService; +import org.mifosplatform.portfolio.savings.service.SavingsAccountChargeReadPlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import com.google.gson.JsonElement; + +@Path("/fixeddepositaccounts") +@Component +@Scope("singleton") +public class FixedDepositAccountsApiResource { + + private final DepositAccountReadPlatformService depositAccountReadPlatformService; + private final PlatformSecurityContext context; + private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final ApiRequestParameterHelper apiRequestParameterHelper; + private final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService; + private final FromJsonHelper fromJsonHelper; + private final DepositAccountPreMatureCalculationPlatformService accountPreMatureCalculationPlatformService; + + @Autowired + public FixedDepositAccountsApiResource(final DepositAccountReadPlatformService depositAccountReadPlatformService, + final PlatformSecurityContext context, final DefaultToApiJsonSerializer toApiJsonSerializer, + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, + final ApiRequestParameterHelper apiRequestParameterHelper, + final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService, final FromJsonHelper fromJsonHelper, + final DepositAccountPreMatureCalculationPlatformService accountPreMatureCalculationPlatformService) { + this.depositAccountReadPlatformService = depositAccountReadPlatformService; + this.context = context; + this.toApiJsonSerializer = toApiJsonSerializer; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + this.apiRequestParameterHelper = apiRequestParameterHelper; + this.savingsAccountChargeReadPlatformService = savingsAccountChargeReadPlatformService; + this.fromJsonHelper = fromJsonHelper; + this.accountPreMatureCalculationPlatformService = accountPreMatureCalculationPlatformService; + } + + @GET + @Path("template") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String template(@QueryParam("clientId") final Long clientId, @QueryParam("groupId") final Long groupId, + @QueryParam("productId") final Long productId, + @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly, + @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME); + + final DepositAccountData account = this.depositAccountReadPlatformService.retrieveTemplate(DepositAccountType.FIXED_DEPOSIT, + clientId, groupId, productId, staffInSelectedOfficeOnly); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, account, DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS); + } + + @GET + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveAll(@Context final UriInfo uriInfo, @QueryParam("paged") final Boolean paged, + @QueryParam("offset") final Integer offset, @QueryParam("limit") final Integer limit, + @QueryParam("orderBy") final String orderBy, @QueryParam("sortOrder") final String sortOrder) { + + this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME); + final PaginationParameters paginationParameters = PaginationParameters.instance(paged, offset, limit, orderBy, sortOrder); + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + if (paginationParameters.isPaged()) { + final Page account = this.depositAccountReadPlatformService.retrieveAllPaged( + DepositAccountType.FIXED_DEPOSIT, paginationParameters); + return this.toApiJsonSerializer.serialize(settings, account, + DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS); + } + + final Collection account = this.depositAccountReadPlatformService.retrieveAll(DepositAccountType.FIXED_DEPOSIT, + paginationParameters); + + return this.toApiJsonSerializer.serialize(settings, account, DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS); + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String submitApplication(final String apiRequestBodyAsJson) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().createFixedDepositAccount().withJson(apiRequestBodyAsJson) + .build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + @GET + @Path("{accountId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveOne(@PathParam("accountId") final Long accountId, + @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly, + @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME); + + final FixedDepositAccountData account = (FixedDepositAccountData) this.depositAccountReadPlatformService.retrieveOneWithChartSlabs( + DepositAccountType.FIXED_DEPOSIT, accountId); + + final Set mandatoryResponseParameters = new HashSet(); + final FixedDepositAccountData accountTemplate = populateTemplateAndAssociations(accountId, account, staffInSelectedOfficeOnly, + uriInfo, mandatoryResponseParameters); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(), + mandatoryResponseParameters); + return this.toApiJsonSerializer.serialize(settings, accountTemplate, + DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS); + } + + private FixedDepositAccountData populateTemplateAndAssociations(final Long accountId, final FixedDepositAccountData savingsAccount, + final boolean staffInSelectedOfficeOnly, final UriInfo uriInfo, final Set mandatoryResponseParameters) { + + Collection transactions = null; + Collection charges = null; + + final Set associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); + if (!associationParameters.isEmpty()) { + + if (associationParameters.contains("all")) { + associationParameters.addAll(Arrays.asList(SavingsApiConstants.transactions, SavingsApiConstants.charges)); + } + + if (associationParameters.contains(SavingsApiConstants.transactions)) { + mandatoryResponseParameters.add(SavingsApiConstants.transactions); + final Collection currentTransactions = this.depositAccountReadPlatformService + .retrieveAllTransactions(DepositAccountType.FIXED_DEPOSIT, accountId); + if (!CollectionUtils.isEmpty(currentTransactions)) { + transactions = currentTransactions; + } + } + + if (associationParameters.contains(SavingsApiConstants.charges)) { + mandatoryResponseParameters.add(SavingsApiConstants.charges); + final Collection currentCharges = this.savingsAccountChargeReadPlatformService + .retrieveSavingsAccountCharges(accountId); + if (!CollectionUtils.isEmpty(currentCharges)) { + charges = currentCharges; + } + } + } + + FixedDepositAccountData templateData = null; + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + if (settings.isTemplate()) { + templateData = (FixedDepositAccountData) this.depositAccountReadPlatformService.retrieveTemplate( + DepositAccountType.FIXED_DEPOSIT, savingsAccount.clientId(), savingsAccount.groupId(), savingsAccount.productId(), + staffInSelectedOfficeOnly); + } + + return FixedDepositAccountData.withTemplateOptions(savingsAccount, templateData, transactions, charges); + } + + @PUT + @Path("{accountId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String update(@PathParam("accountId") final Long accountId, final String apiRequestBodyAsJson) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().updateFixedDepositAccount(accountId) + .withJson(apiRequestBodyAsJson).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + @POST + @Path("{accountId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String handleCommands(@PathParam("accountId") final Long accountId, @QueryParam("command") final String commandParam, + @Context final UriInfo uriInfo, final String apiRequestBodyAsJson) { + + String jsonApiRequest = apiRequestBodyAsJson; + if (StringUtils.isBlank(jsonApiRequest)) { + jsonApiRequest = "{}"; + } + + final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(jsonApiRequest); + + CommandProcessingResult result = null; + if (is(commandParam, "reject")) { + final CommandWrapper commandRequest = builder.rejectFixedDepositAccountApplication(accountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "withdrawnByApplicant")) { + final CommandWrapper commandRequest = builder.withdrawFixedDepositAccountApplication(accountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "approve")) { + final CommandWrapper commandRequest = builder.approveFixedDepositAccountApplication(accountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "undoapproval")) { + final CommandWrapper commandRequest = builder.undoFixedDepositAccountApplication(accountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "activate")) { + final CommandWrapper commandRequest = builder.fixedDepositAccountActivation(accountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "calculateInterest")) { + final CommandWrapper commandRequest = builder.withNoJsonBody().fixedDepositAccountInterestCalculation(accountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "postInterest")) { + final CommandWrapper commandRequest = builder.fixedDepositAccountInterestPosting(accountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "close")) { + final CommandWrapper commandRequest = builder.closeFixedDepositAccount(accountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "prematureClose")) { + final CommandWrapper commandRequest = builder.prematureCloseFixedDepositAccount(accountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "calculatePrematureAmount")) { + final JsonElement parsedQuery = this.fromJsonHelper.parse(apiRequestBodyAsJson); + final JsonQuery query = JsonQuery.from(apiRequestBodyAsJson, parsedQuery, this.fromJsonHelper); + final DepositAccountData account = this.accountPreMatureCalculationPlatformService.calculatePreMatureAmount(accountId, query, + DepositAccountType.FIXED_DEPOSIT); + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, account, + DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS); + } + + if (result == null) { throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "reject", + "withdrawnByApplicant", "approve", "undoapproval", "activate", "calculateInterest", "postInterest", "close", + "prematureClose", "calculatePrematureAmount" }); } + + return this.toApiJsonSerializer.serialize(result); + } + + private boolean is(final String commandParam, final String commandValue) { + return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue); + } + + @DELETE + @Path("{accountId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String delete(@PathParam("accountId") final Long accountId) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteFixedDepositAccount(accountId).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + @GET + @Path("{accountId}/template") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String accountClosureTemplate(@PathParam("accountId") final Long accountId, @QueryParam("command") final String commandParam, + @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME); + DepositAccountData account = null; + if (is(commandParam, "close")) { + account = this.depositAccountReadPlatformService.retrieveOneWithClosureTemplate(DepositAccountType.FIXED_DEPOSIT, accountId); + } + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, account, DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/FixedDepositProductsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/FixedDepositProductsApiResource.java new file mode 100644 index 000000000..ed2d7ffc8 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/FixedDepositProductsApiResource.java @@ -0,0 +1,316 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.api; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; + +import org.mifosplatform.accounting.common.AccountingDropdownReadPlatformService; +import org.mifosplatform.accounting.common.AccountingEnumerations; +import org.mifosplatform.accounting.common.AccountingRuleType; +import org.mifosplatform.accounting.glaccount.data.GLAccountData; +import org.mifosplatform.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper; +import org.mifosplatform.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper; +import org.mifosplatform.accounting.producttoaccountmapping.service.ProductToGLAccountMappingReadPlatformService; +import org.mifosplatform.commands.domain.CommandWrapper; +import org.mifosplatform.commands.service.CommandWrapperBuilder; +import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; +import org.mifosplatform.infrastructure.codes.data.CodeValueData; +import org.mifosplatform.infrastructure.codes.service.CodeValueReadPlatformService; +import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.organisation.monetary.data.CurrencyData; +import org.mifosplatform.organisation.monetary.service.CurrencyReadPlatformService; +import org.mifosplatform.portfolio.charge.data.ChargeData; +import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService; +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartData; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartReadPlatformService; +import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.DepositsApiConstants; +import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; +import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType; +import org.mifosplatform.portfolio.savings.data.FixedDepositProductData; +import org.mifosplatform.portfolio.savings.service.DepositProductReadPlatformService; +import org.mifosplatform.portfolio.savings.service.DepositsDropdownReadPlatformService; +import org.mifosplatform.portfolio.savings.service.SavingsDropdownReadPlatformService; +import org.mifosplatform.portfolio.savings.service.SavingsEnumerations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +@Path("/fixeddepositproducts") +@Component +@Scope("singleton") +public class FixedDepositProductsApiResource { + + private final DepositProductReadPlatformService depositProductReadPlatformService; + private final SavingsDropdownReadPlatformService dropdownReadPlatformService; + private final CurrencyReadPlatformService currencyReadPlatformService; + private final PlatformSecurityContext context; + private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final ApiRequestParameterHelper apiRequestParameterHelper; + private final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService; + private final CodeValueReadPlatformService codeValueReadPlatformService; + private final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService; + private final ChargeReadPlatformService chargeReadPlatformService; + private final InterestRateChartReadPlatformService chartReadPlatformService; + private final InterestRateChartReadPlatformService interestRateChartReadPlatformService; + private final DepositsDropdownReadPlatformService depositsDropdownReadPlatformService; + + @Autowired + public FixedDepositProductsApiResource(final DepositProductReadPlatformService depositProductReadPlatformService, + final SavingsDropdownReadPlatformService dropdownReadPlatformService, + final CurrencyReadPlatformService currencyReadPlatformService, final PlatformSecurityContext context, + final DefaultToApiJsonSerializer toApiJsonSerializer, + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, + final ApiRequestParameterHelper apiRequestParameterHelper, final CodeValueReadPlatformService codeValueReadPlatformService, + final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService, + final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService, + final ChargeReadPlatformService chargeReadPlatformService, final InterestRateChartReadPlatformService chartReadPlatformService, + final InterestRateChartReadPlatformService interestRateChartReadPlatformService, + final DepositsDropdownReadPlatformService depositsDropdownReadPlatformService) { + this.depositProductReadPlatformService = depositProductReadPlatformService; + this.dropdownReadPlatformService = dropdownReadPlatformService; + this.currencyReadPlatformService = currencyReadPlatformService; + this.context = context; + this.toApiJsonSerializer = toApiJsonSerializer; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + this.apiRequestParameterHelper = apiRequestParameterHelper; + this.codeValueReadPlatformService = codeValueReadPlatformService; + this.accountingDropdownReadPlatformService = accountingDropdownReadPlatformService; + this.accountMappingReadPlatformService = accountMappingReadPlatformService; + this.chargeReadPlatformService = chargeReadPlatformService; + this.chartReadPlatformService = chartReadPlatformService; + this.interestRateChartReadPlatformService = interestRateChartReadPlatformService; + this.depositsDropdownReadPlatformService = depositsDropdownReadPlatformService; + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String create(final String apiRequestBodyAsJson) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().createFixedDepositProduct().withJson(apiRequestBodyAsJson) + .build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + @PUT + @Path("{productId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String update(@PathParam("productId") final Long productId, final String apiRequestBodyAsJson) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().updateFixedDepositProduct(productId) + .withJson(apiRequestBodyAsJson).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + + } + + @GET + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveAll(@Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME); + + @SuppressWarnings({ "unchecked", "rawtypes" }) + final Collection products = (Collection) this.depositProductReadPlatformService + .retrieveAll(DepositAccountType.FIXED_DEPOSIT); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, products, DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_RESPONSE_DATA_PARAMETERS); + } + + @GET + @Path("{productId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveOne(@PathParam("productId") final Long productId, @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME); + + FixedDepositProductData fixedDepositProductData = (FixedDepositProductData) this.depositProductReadPlatformService.retrieveOne( + DepositAccountType.FIXED_DEPOSIT, productId); + + final Collection charges = this.chargeReadPlatformService.retrieveSavingsProductCharges(productId); + fixedDepositProductData = FixedDepositProductData.withCharges(fixedDepositProductData, charges); + + final Collection charts = this.chartReadPlatformService.retrieveAllWithSlabsWithTemplate(productId); + fixedDepositProductData = FixedDepositProductData.withInterestChart(fixedDepositProductData, charts); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + if (fixedDepositProductData.hasAccountingEnabled()) { + final Map accountingMappings = this.accountMappingReadPlatformService + .fetchAccountMappingDetailsForSavingsProduct(productId, fixedDepositProductData.accountingRuleTypeId()); + final Collection paymentChannelToFundSourceMappings = this.accountMappingReadPlatformService + .fetchPaymentTypeToFundSourceMappingsForSavingsProduct(productId); + Collection feeToGLAccountMappings = this.accountMappingReadPlatformService + .fetchFeeToIncomeAccountMappingsForSavingsProduct(productId); + Collection penaltyToGLAccountMappings = this.accountMappingReadPlatformService + .fetchPenaltyToIncomeAccountMappingsForSavingsProduct(productId); + fixedDepositProductData = FixedDepositProductData.withAccountingDetails(fixedDepositProductData, accountingMappings, + paymentChannelToFundSourceMappings, feeToGLAccountMappings, penaltyToGLAccountMappings); + } + + if (settings.isTemplate()) { + fixedDepositProductData = handleTemplateRelatedData(fixedDepositProductData); + } + + return this.toApiJsonSerializer.serialize(settings, fixedDepositProductData, + DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_RESPONSE_DATA_PARAMETERS); + } + + @GET + @Path("template") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveTemplate(@Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME); + + final FixedDepositProductData fixedDepositProduct = handleTemplateRelatedData(null); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, fixedDepositProduct, + DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_RESPONSE_DATA_PARAMETERS); + } + + private FixedDepositProductData handleTemplateRelatedData(final FixedDepositProductData savingsProduct) { + + final EnumOptionData interestCompoundingPeriodType = SavingsEnumerations + .compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.DAILY); + + final EnumOptionData interestPostingPeriodType = SavingsEnumerations + .interestPostingPeriodType(SavingsPostingInterestPeriodType.MONTHLY); + + final EnumOptionData interestCalculationType = SavingsEnumerations + .interestCalculationType(SavingsInterestCalculationType.DAILY_BALANCE); + + final EnumOptionData interestCalculationDaysInYearType = SavingsEnumerations + .interestCalculationDaysInYearType(SavingsInterestCalculationDaysInYearType.DAYS_365); + + final EnumOptionData accountingRule = AccountingEnumerations.accountingRuleType(AccountingRuleType.NONE); + + CurrencyData currency = CurrencyData.blank(); + final Collection currencyOptions = this.currencyReadPlatformService.retrieveAllowedCurrencies(); + if (currencyOptions.size() == 1) { + currency = new ArrayList(currencyOptions).get(0); + } + + final Collection interestCompoundingPeriodTypeOptions = this.dropdownReadPlatformService + .retrieveCompoundingInterestPeriodTypeOptions(); + + final Collection interestPostingPeriodTypeOptions = this.dropdownReadPlatformService + .retrieveInterestPostingPeriodTypeOptions(); + + final Collection interestCalculationTypeOptions = this.dropdownReadPlatformService + .retrieveInterestCalculationTypeOptions(); + + final Collection interestCalculationDaysInYearTypeOptions = this.dropdownReadPlatformService + .retrieveInterestCalculationDaysInYearTypeOptions(); + + final Collection lockinPeriodFrequencyTypeOptions = this.dropdownReadPlatformService + .retrieveLockinPeriodFrequencyTypeOptions(); + + final Collection withdrawalFeeTypeOptions = this.dropdownReadPlatformService.retrievewithdrawalFeeTypeOptions(); + + final Collection paymentTypeOptions = this.codeValueReadPlatformService + .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); + + final Collection accountingRuleOptions = this.accountingDropdownReadPlatformService + .retrieveAccountingRuleTypeOptions(); + + final Map> accountingMappingOptions = this.accountingDropdownReadPlatformService + .retrieveAccountMappingOptionsForSavingsProducts(); + + final Collection preClosurePenalInterestOnTypeOptions = this.depositsDropdownReadPlatformService + .retrievePreClosurePenalInterestOnTypeOptions(); + + final Collection interestFreePeriodTypeOptions = this.depositsDropdownReadPlatformService + .retrieveInterestFreePeriodFrequencyTypeOptions(); + + final Collection depositTermTypeOptions = this.depositsDropdownReadPlatformService.retrieveDepositTermTypeOptions(); + + final Collection inMultiplesOfDepositTermTypeOptions = this.depositsDropdownReadPlatformService + .retrieveInMultiplesOfDepositTermTypeOptions(); + + // charges + final boolean feeChargesOnly = true; + Collection chargeOptions = this.chargeReadPlatformService.retrieveSavingsAccountApplicableCharges(feeChargesOnly); + chargeOptions = CollectionUtils.isEmpty(chargeOptions) ? null : chargeOptions; + + Collection penaltyOptions = this.chargeReadPlatformService.retrieveSavingsAccountApplicablePenalties(); + penaltyOptions = CollectionUtils.isEmpty(penaltyOptions) ? null : penaltyOptions; + + // interest rate chart template + final InterestRateChartData chartTemplate = this.interestRateChartReadPlatformService.template(); + + FixedDepositProductData fixedDepositProductToReturn = null; + if (savingsProduct != null) { + fixedDepositProductToReturn = FixedDepositProductData.withTemplate(savingsProduct, currencyOptions, + interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, + paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, chargeOptions, penaltyOptions, chartTemplate, + interestFreePeriodTypeOptions, preClosurePenalInterestOnTypeOptions, depositTermTypeOptions, + inMultiplesOfDepositTermTypeOptions); + } else { + fixedDepositProductToReturn = FixedDepositProductData.template(currency, interestCompoundingPeriodType, + interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, accountingRule, currencyOptions, + interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, + paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, chargeOptions, penaltyOptions, chartTemplate, + interestFreePeriodTypeOptions, preClosurePenalInterestOnTypeOptions, depositTermTypeOptions, + inMultiplesOfDepositTermTypeOptions); + } + + return fixedDepositProductToReturn; + } + + @DELETE + @Path("{productId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String delete(@PathParam("productId") final Long productId) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteFixedDepositProduct(productId).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/RecurringDepositAccountTransactionsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/RecurringDepositAccountTransactionsApiResource.java new file mode 100644 index 000000000..def6c996a --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/RecurringDepositAccountTransactionsApiResource.java @@ -0,0 +1,185 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.api; + +import java.util.Collection; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; + +import org.apache.commons.lang.StringUtils; +import org.mifosplatform.commands.domain.CommandWrapper; +import org.mifosplatform.commands.service.CommandWrapperBuilder; +import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; +import org.mifosplatform.infrastructure.codes.data.CodeValueData; +import org.mifosplatform.infrastructure.codes.service.CodeValueReadPlatformService; +import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.exception.UnrecognizedQueryParamException; +import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.DepositsApiConstants; +import org.mifosplatform.portfolio.savings.SavingsApiConstants; +import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionData; +import org.mifosplatform.portfolio.savings.service.DepositAccountReadPlatformService; +import org.mifosplatform.portfolio.savings.service.SavingsAccountReadPlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Path("/recurringdepositaccounts/{recurringDepositAccountId}/transactions") +@Component +@Scope("singleton") +public class RecurringDepositAccountTransactionsApiResource { + + private final PlatformSecurityContext context; + private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final ApiRequestParameterHelper apiRequestParameterHelper; + private final SavingsAccountReadPlatformService savingsAccountReadPlatformService; + private final DepositAccountReadPlatformService depositAccountReadPlatformService; + private final CodeValueReadPlatformService codeValueReadPlatformService; + + @Autowired + public RecurringDepositAccountTransactionsApiResource(final PlatformSecurityContext context, + final DefaultToApiJsonSerializer toApiJsonSerializer, + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, + final ApiRequestParameterHelper apiRequestParameterHelper, + final SavingsAccountReadPlatformService savingsAccountReadPlatformService, + final DepositAccountReadPlatformService depositAccountReadPlatformService, + final CodeValueReadPlatformService codeValueReadPlatformService) { + this.context = context; + this.toApiJsonSerializer = toApiJsonSerializer; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + this.apiRequestParameterHelper = apiRequestParameterHelper; + this.savingsAccountReadPlatformService = savingsAccountReadPlatformService; + this.depositAccountReadPlatformService = depositAccountReadPlatformService; + this.codeValueReadPlatformService = codeValueReadPlatformService; + } + + private boolean is(final String commandParam, final String commandValue) { + return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue); + } + + @GET + @Path("template") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveTemplate(@PathParam("recurringDepositAccountId") final Long recurringDepositAccountId, + @QueryParam("command") final String commandParam, @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME); + + SavingsAccountTransactionData savingsAccount = null; + + if (is(commandParam, "deposit")) { + savingsAccount = this.depositAccountReadPlatformService + .retrieveRecurringAccountDepositTransactionTemplate(recurringDepositAccountId); + } else if (is(commandParam, "withdrawal")) { + + } else { + throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "deposit", "withdrawal" }); + } + + final Collection paymentTypeOptions = this.codeValueReadPlatformService + .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); + savingsAccount = SavingsAccountTransactionData.templateOnTop(savingsAccount, paymentTypeOptions); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, savingsAccount, + SavingsApiConstants.SAVINGS_TRANSACTION_RESPONSE_DATA_PARAMETERS); + } + + @GET + @Path("{transactionId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveOne(@PathParam("recurringDepositAccountId") final Long recurringDepositAccountId, + @PathParam("transactionId") final Long transactionId, @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME); + SavingsAccountTransactionData transactionData = this.savingsAccountReadPlatformService.retrieveSavingsTransaction( + recurringDepositAccountId, transactionId, DepositAccountType.RECURRING_DEPOSIT); + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + if (settings.isTemplate()) { + final Collection paymentTypeOptions = this.codeValueReadPlatformService + .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); + transactionData = SavingsAccountTransactionData.templateOnTop(transactionData, paymentTypeOptions); + } + + return this.toApiJsonSerializer.serialize(settings, transactionData, + DepositsApiConstants.FIXED_DEPOSIT_TRANSACTION_RESPONSE_DATA_PARAMETERS); + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String transaction(@PathParam("recurringDepositAccountId") final Long recurringDepositAccountId, + @QueryParam("command") final String commandParam, final String apiRequestBodyAsJson) { + + final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson); + + CommandProcessingResult result = null; + + if (is(commandParam, "deposit")) { + final CommandWrapper commandRequest = builder.recurringAccountDeposit(recurringDepositAccountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "withdrawal")) { + final CommandWrapper commandRequest = builder.recurringAccountWithdrawal(recurringDepositAccountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } + + if (result == null) { + // + throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "deposit", "withdrawal" }); + } + + return this.toApiJsonSerializer.serialize(result); + } + + @POST + @Path("{transactionId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String handleTransactionCommands(@PathParam("recurringDepositAccountId") final Long recurringDepositAccountId, + @PathParam("transactionId") final Long transactionId, @QueryParam("command") final String commandParam, + final String apiRequestBodyAsJson) { + + String jsonApiRequest = apiRequestBodyAsJson; + if (StringUtils.isBlank(jsonApiRequest)) { + jsonApiRequest = "{}"; + } + + final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(jsonApiRequest); + + CommandProcessingResult result = null; + if (is(commandParam, DepositsApiConstants.COMMAND_UNDO_TRANSACTION)) { + final CommandWrapper commandRequest = builder.undoSavingsAccountTransaction(recurringDepositAccountId, transactionId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, DepositsApiConstants.COMMAND_ADJUST_TRANSACTION)) { + final CommandWrapper commandRequest = builder.adjustRecurringAccountTransaction(recurringDepositAccountId, transactionId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } + + if (result == null) { + // + throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "undo", "modify" }); + } + + return this.toApiJsonSerializer.serialize(result); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/RecurringDepositAccountsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/RecurringDepositAccountsApiResource.java new file mode 100644 index 000000000..372ede8f8 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/RecurringDepositAccountsApiResource.java @@ -0,0 +1,324 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.api; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; + +import org.apache.commons.lang.StringUtils; +import org.mifosplatform.commands.domain.CommandWrapper; +import org.mifosplatform.commands.service.CommandWrapperBuilder; +import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; +import org.mifosplatform.infrastructure.core.api.ApiParameterHelper; +import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; +import org.mifosplatform.infrastructure.core.api.JsonQuery; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.data.PaginationParameters; +import org.mifosplatform.infrastructure.core.exception.UnrecognizedQueryParamException; +import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.infrastructure.core.service.Page; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.DepositsApiConstants; +import org.mifosplatform.portfolio.savings.SavingsApiConstants; +import org.mifosplatform.portfolio.savings.data.DepositAccountData; +import org.mifosplatform.portfolio.savings.data.RecurringDepositAccountData; +import org.mifosplatform.portfolio.savings.data.SavingsAccountChargeData; +import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionData; +import org.mifosplatform.portfolio.savings.service.DepositAccountPreMatureCalculationPlatformService; +import org.mifosplatform.portfolio.savings.service.DepositAccountReadPlatformService; +import org.mifosplatform.portfolio.savings.service.SavingsAccountChargeReadPlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import com.google.gson.JsonElement; + +@Path("/recurringdepositaccounts") +@Component +@Scope("singleton") +public class RecurringDepositAccountsApiResource { + + private final DepositAccountReadPlatformService depositAccountReadPlatformService; + private final PlatformSecurityContext context; + private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final ApiRequestParameterHelper apiRequestParameterHelper; + private final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService; + private final FromJsonHelper fromJsonHelper; + private final DepositAccountPreMatureCalculationPlatformService accountPreMatureCalculationPlatformService; + + @Autowired + public RecurringDepositAccountsApiResource(final DepositAccountReadPlatformService depositAccountReadPlatformService, + final PlatformSecurityContext context, final DefaultToApiJsonSerializer toApiJsonSerializer, + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, + final ApiRequestParameterHelper apiRequestParameterHelper, + final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService, final FromJsonHelper fromJsonHelper, + final DepositAccountPreMatureCalculationPlatformService accountPreMatureCalculationPlatformService) { + this.depositAccountReadPlatformService = depositAccountReadPlatformService; + this.context = context; + this.toApiJsonSerializer = toApiJsonSerializer; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + this.apiRequestParameterHelper = apiRequestParameterHelper; + this.savingsAccountChargeReadPlatformService = savingsAccountChargeReadPlatformService; + this.fromJsonHelper = fromJsonHelper; + this.accountPreMatureCalculationPlatformService = accountPreMatureCalculationPlatformService; + } + + @GET + @Path("template") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String template(@QueryParam("clientId") final Long clientId, @QueryParam("groupId") final Long groupId, + @QueryParam("productId") final Long productId, + @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly, + @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME); + + final DepositAccountData account = this.depositAccountReadPlatformService.retrieveTemplate(DepositAccountType.RECURRING_DEPOSIT, + clientId, groupId, productId, staffInSelectedOfficeOnly); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, account, + DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS); + } + + @GET + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveAll(@Context final UriInfo uriInfo, @QueryParam("paged") final Boolean paged, + @QueryParam("offset") final Integer offset, @QueryParam("limit") final Integer limit, + @QueryParam("orderBy") final String orderBy, @QueryParam("sortOrder") final String sortOrder) { + + this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME); + final PaginationParameters paginationParameters = PaginationParameters.instance(paged, offset, limit, orderBy, sortOrder); + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + if (paginationParameters.isPaged()) { + final Page account = this.depositAccountReadPlatformService.retrieveAllPaged( + DepositAccountType.RECURRING_DEPOSIT, paginationParameters); + return this.toApiJsonSerializer.serialize(settings, account, + DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS); + } + + final Collection account = this.depositAccountReadPlatformService.retrieveAll( + DepositAccountType.RECURRING_DEPOSIT, paginationParameters); + + return this.toApiJsonSerializer.serialize(settings, account, + DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS); + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String submitApplication(final String apiRequestBodyAsJson) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().createRecurringDepositAccount().withJson(apiRequestBodyAsJson) + .build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + @GET + @Path("{accountId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveOne(@PathParam("accountId") final Long accountId, + @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly, + @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME); + + final RecurringDepositAccountData account = (RecurringDepositAccountData) this.depositAccountReadPlatformService + .retrieveOneWithChartSlabs(DepositAccountType.RECURRING_DEPOSIT, accountId); + + final Set mandatoryResponseParameters = new HashSet(); + final RecurringDepositAccountData accountTemplate = populateTemplateAndAssociations(accountId, account, staffInSelectedOfficeOnly, + uriInfo, mandatoryResponseParameters); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(), + mandatoryResponseParameters); + return this.toApiJsonSerializer.serialize(settings, accountTemplate, + DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS); + } + + private RecurringDepositAccountData populateTemplateAndAssociations(final Long accountId, + final RecurringDepositAccountData savingsAccount, final boolean staffInSelectedOfficeOnly, final UriInfo uriInfo, + final Set mandatoryResponseParameters) { + + Collection transactions = null; + Collection charges = null; + + final Set associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); + if (!associationParameters.isEmpty()) { + + if (associationParameters.contains("all")) { + associationParameters.addAll(Arrays.asList(SavingsApiConstants.transactions, SavingsApiConstants.charges)); + } + + if (associationParameters.contains(SavingsApiConstants.transactions)) { + mandatoryResponseParameters.add(SavingsApiConstants.transactions); + final Collection currentTransactions = this.depositAccountReadPlatformService + .retrieveAllTransactions(DepositAccountType.RECURRING_DEPOSIT, accountId); + if (!CollectionUtils.isEmpty(currentTransactions)) { + transactions = currentTransactions; + } + } + + if (associationParameters.contains(SavingsApiConstants.charges)) { + mandatoryResponseParameters.add(SavingsApiConstants.charges); + final Collection currentCharges = this.savingsAccountChargeReadPlatformService + .retrieveSavingsAccountCharges(accountId); + if (!CollectionUtils.isEmpty(currentCharges)) { + charges = currentCharges; + } + } + } + + RecurringDepositAccountData templateData = null; + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + if (settings.isTemplate()) { + templateData = (RecurringDepositAccountData) this.depositAccountReadPlatformService.retrieveTemplate( + DepositAccountType.RECURRING_DEPOSIT, savingsAccount.clientId(), savingsAccount.groupId(), savingsAccount.productId(), + staffInSelectedOfficeOnly); + } + + return RecurringDepositAccountData.withTemplateOptions(savingsAccount, templateData, transactions, charges); + } + + @PUT + @Path("{accountId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String update(@PathParam("accountId") final Long accountId, final String apiRequestBodyAsJson) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().updateRecurringDepositAccount(accountId) + .withJson(apiRequestBodyAsJson).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + @POST + @Path("{accountId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String handleCommands(@PathParam("accountId") final Long accountId, @QueryParam("command") final String commandParam, + @Context final UriInfo uriInfo, final String apiRequestBodyAsJson) { + + String jsonApiRequest = apiRequestBodyAsJson; + if (StringUtils.isBlank(jsonApiRequest)) { + jsonApiRequest = "{}"; + } + + final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(jsonApiRequest); + + CommandProcessingResult result = null; + if (is(commandParam, "reject")) { + final CommandWrapper commandRequest = builder.rejectRecurringDepositAccountApplication(accountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "withdrawnByApplicant")) { + final CommandWrapper commandRequest = builder.withdrawRecurringDepositAccountApplication(accountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "approve")) { + final CommandWrapper commandRequest = builder.approveRecurringDepositAccountApplication(accountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "undoapproval")) { + final CommandWrapper commandRequest = builder.undoRecurringDepositAccountApplication(accountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "activate")) { + final CommandWrapper commandRequest = builder.recurringDepositAccountActivation(accountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "calculateInterest")) { + final CommandWrapper commandRequest = builder.withNoJsonBody().recurringDepositAccountInterestCalculation(accountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "postInterest")) { + final CommandWrapper commandRequest = builder.recurringDepositAccountInterestPosting(accountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "close")) { + final CommandWrapper commandRequest = builder.closeRecurringDepositAccount(accountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "prematureClose")) { + final CommandWrapper commandRequest = builder.prematureCloseRecurringDepositAccount(accountId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "calculatePrematureAmount")) { + final JsonElement parsedQuery = this.fromJsonHelper.parse(apiRequestBodyAsJson); + final JsonQuery query = JsonQuery.from(apiRequestBodyAsJson, parsedQuery, this.fromJsonHelper); + final DepositAccountData account = this.accountPreMatureCalculationPlatformService.calculatePreMatureAmount(accountId, query, + DepositAccountType.RECURRING_DEPOSIT); + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, account, + DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS); + } + + if (result == null) { throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "reject", + "withdrawnByApplicant", "approve", "undoapproval", "activate", "calculateInterest", "postInterest", "close", + "prematureClose", "calculatePrematureAmount" }); } + + return this.toApiJsonSerializer.serialize(result); + } + + private boolean is(final String commandParam, final String commandValue) { + return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue); + } + + @DELETE + @Path("{accountId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String delete(@PathParam("accountId") final Long accountId) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteRecurringDepositAccount(accountId).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + @GET + @Path("{accountId}/template") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String accountClosureTemplate(@PathParam("accountId") final Long accountId, @QueryParam("command") final String commandParam, + @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME); + DepositAccountData account = null; + if (is(commandParam, "close")) { + account = this.depositAccountReadPlatformService + .retrieveOneWithClosureTemplate(DepositAccountType.RECURRING_DEPOSIT, accountId); + + } + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, account, + DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/RecurringDepositProductsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/RecurringDepositProductsApiResource.java new file mode 100644 index 000000000..5b13d0264 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/RecurringDepositProductsApiResource.java @@ -0,0 +1,318 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.api; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; + +import org.mifosplatform.accounting.common.AccountingDropdownReadPlatformService; +import org.mifosplatform.accounting.common.AccountingEnumerations; +import org.mifosplatform.accounting.common.AccountingRuleType; +import org.mifosplatform.accounting.glaccount.data.GLAccountData; +import org.mifosplatform.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper; +import org.mifosplatform.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper; +import org.mifosplatform.accounting.producttoaccountmapping.service.ProductToGLAccountMappingReadPlatformService; +import org.mifosplatform.commands.domain.CommandWrapper; +import org.mifosplatform.commands.service.CommandWrapperBuilder; +import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; +import org.mifosplatform.infrastructure.codes.data.CodeValueData; +import org.mifosplatform.infrastructure.codes.service.CodeValueReadPlatformService; +import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.organisation.monetary.data.CurrencyData; +import org.mifosplatform.organisation.monetary.service.CurrencyReadPlatformService; +import org.mifosplatform.portfolio.charge.data.ChargeData; +import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService; +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartData; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartReadPlatformService; +import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.DepositsApiConstants; +import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; +import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType; +import org.mifosplatform.portfolio.savings.data.RecurringDepositProductData; +import org.mifosplatform.portfolio.savings.service.DepositProductReadPlatformService; +import org.mifosplatform.portfolio.savings.service.DepositsDropdownReadPlatformService; +import org.mifosplatform.portfolio.savings.service.SavingsDropdownReadPlatformService; +import org.mifosplatform.portfolio.savings.service.SavingsEnumerations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +@Path("/recurringdepositproducts") +@Component +@Scope("singleton") +public class RecurringDepositProductsApiResource { + + private final DepositProductReadPlatformService depositProductReadPlatformService; + private final SavingsDropdownReadPlatformService dropdownReadPlatformService; + private final CurrencyReadPlatformService currencyReadPlatformService; + private final PlatformSecurityContext context; + private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final ApiRequestParameterHelper apiRequestParameterHelper; + private final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService; + private final CodeValueReadPlatformService codeValueReadPlatformService; + private final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService; + private final ChargeReadPlatformService chargeReadPlatformService; + private final InterestRateChartReadPlatformService chartReadPlatformService; + private final InterestRateChartReadPlatformService interestRateChartReadPlatformService; + private final DepositsDropdownReadPlatformService depositsDropdownReadPlatformService; + + @Autowired + public RecurringDepositProductsApiResource(final DepositProductReadPlatformService depositProductReadPlatformService, + final SavingsDropdownReadPlatformService dropdownReadPlatformService, + final CurrencyReadPlatformService currencyReadPlatformService, final PlatformSecurityContext context, + final DefaultToApiJsonSerializer toApiJsonSerializer, + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, + final ApiRequestParameterHelper apiRequestParameterHelper, final CodeValueReadPlatformService codeValueReadPlatformService, + final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService, + final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService, + final ChargeReadPlatformService chargeReadPlatformService, final InterestRateChartReadPlatformService chartReadPlatformService, + final InterestRateChartReadPlatformService interestRateChartReadPlatformService, + final DepositsDropdownReadPlatformService depositsDropdownReadPlatformService) { + this.depositProductReadPlatformService = depositProductReadPlatformService; + this.dropdownReadPlatformService = dropdownReadPlatformService; + this.currencyReadPlatformService = currencyReadPlatformService; + this.context = context; + this.toApiJsonSerializer = toApiJsonSerializer; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + this.apiRequestParameterHelper = apiRequestParameterHelper; + this.codeValueReadPlatformService = codeValueReadPlatformService; + this.accountingDropdownReadPlatformService = accountingDropdownReadPlatformService; + this.accountMappingReadPlatformService = accountMappingReadPlatformService; + this.chargeReadPlatformService = chargeReadPlatformService; + this.chartReadPlatformService = chartReadPlatformService; + this.interestRateChartReadPlatformService = interestRateChartReadPlatformService; + this.depositsDropdownReadPlatformService = depositsDropdownReadPlatformService; + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String create(final String apiRequestBodyAsJson) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().createRecurringDepositProduct().withJson(apiRequestBodyAsJson) + .build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + @PUT + @Path("{productId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String update(@PathParam("productId") final Long productId, final String apiRequestBodyAsJson) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().updateRecurringDepositProduct(productId) + .withJson(apiRequestBodyAsJson).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + + } + + @GET + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveAll(@Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME); + + @SuppressWarnings({ "unchecked", "rawtypes" }) + final Collection products = (Collection) this.depositProductReadPlatformService + .retrieveAll(DepositAccountType.RECURRING_DEPOSIT); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, products, + DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESPONSE_DATA_PARAMETERS); + } + + @GET + @Path("{productId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveOne(@PathParam("productId") final Long productId, @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME); + + RecurringDepositProductData recurringDepositProductData = (RecurringDepositProductData) this.depositProductReadPlatformService + .retrieveOne(DepositAccountType.RECURRING_DEPOSIT, productId); + + final Collection charges = this.chargeReadPlatformService.retrieveSavingsProductCharges(productId); + recurringDepositProductData = RecurringDepositProductData.withCharges(recurringDepositProductData, charges); + + final Collection charts = this.chartReadPlatformService.retrieveAllWithSlabsWithTemplate(productId); + recurringDepositProductData = RecurringDepositProductData.withInterestChart(recurringDepositProductData, charts); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + if (recurringDepositProductData.hasAccountingEnabled()) { + final Map accountingMappings = this.accountMappingReadPlatformService + .fetchAccountMappingDetailsForSavingsProduct(productId, recurringDepositProductData.accountingRuleTypeId()); + final Collection paymentChannelToFundSourceMappings = this.accountMappingReadPlatformService + .fetchPaymentTypeToFundSourceMappingsForSavingsProduct(productId); + Collection feeToGLAccountMappings = this.accountMappingReadPlatformService + .fetchFeeToIncomeAccountMappingsForSavingsProduct(productId); + Collection penaltyToGLAccountMappings = this.accountMappingReadPlatformService + .fetchPenaltyToIncomeAccountMappingsForSavingsProduct(productId); + recurringDepositProductData = RecurringDepositProductData.withAccountingDetails(recurringDepositProductData, + accountingMappings, paymentChannelToFundSourceMappings, feeToGLAccountMappings, penaltyToGLAccountMappings); + } + + if (settings.isTemplate()) { + recurringDepositProductData = handleTemplateRelatedData(recurringDepositProductData); + } + + return this.toApiJsonSerializer.serialize(settings, recurringDepositProductData, + DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESPONSE_DATA_PARAMETERS); + } + + @GET + @Path("template") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveTemplate(@Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME); + + final RecurringDepositProductData recurringDepositProduct = handleTemplateRelatedData(null); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, recurringDepositProduct, + DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESPONSE_DATA_PARAMETERS); + } + + private RecurringDepositProductData handleTemplateRelatedData(final RecurringDepositProductData savingsProduct) { + + final EnumOptionData interestCompoundingPeriodType = SavingsEnumerations + .compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.DAILY); + + final EnumOptionData interestPostingPeriodType = SavingsEnumerations + .interestPostingPeriodType(SavingsPostingInterestPeriodType.MONTHLY); + + final EnumOptionData interestCalculationType = SavingsEnumerations + .interestCalculationType(SavingsInterestCalculationType.DAILY_BALANCE); + + final EnumOptionData interestCalculationDaysInYearType = SavingsEnumerations + .interestCalculationDaysInYearType(SavingsInterestCalculationDaysInYearType.DAYS_365); + + final EnumOptionData accountingRule = AccountingEnumerations.accountingRuleType(AccountingRuleType.NONE); + + CurrencyData currency = CurrencyData.blank(); + final Collection currencyOptions = this.currencyReadPlatformService.retrieveAllowedCurrencies(); + if (currencyOptions.size() == 1) { + currency = new ArrayList(currencyOptions).get(0); + } + + final Collection interestCompoundingPeriodTypeOptions = this.dropdownReadPlatformService + .retrieveCompoundingInterestPeriodTypeOptions(); + + final Collection interestPostingPeriodTypeOptions = this.dropdownReadPlatformService + .retrieveInterestPostingPeriodTypeOptions(); + + final Collection interestCalculationTypeOptions = this.dropdownReadPlatformService + .retrieveInterestCalculationTypeOptions(); + + final Collection interestCalculationDaysInYearTypeOptions = this.dropdownReadPlatformService + .retrieveInterestCalculationDaysInYearTypeOptions(); + + final Collection lockinPeriodFrequencyTypeOptions = this.dropdownReadPlatformService + .retrieveLockinPeriodFrequencyTypeOptions(); + + final Collection withdrawalFeeTypeOptions = this.dropdownReadPlatformService.retrievewithdrawalFeeTypeOptions(); + + final Collection paymentTypeOptions = this.codeValueReadPlatformService + .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); + final Collection accountingRuleOptions = this.accountingDropdownReadPlatformService + .retrieveAccountingRuleTypeOptions(); + final Map> accountingMappingOptions = this.accountingDropdownReadPlatformService + .retrieveAccountMappingOptionsForSavingsProducts(); + final Collection preClosurePenalInterestOnTypeOptions = this.depositsDropdownReadPlatformService + .retrievePreClosurePenalInterestOnTypeOptions(); + final Collection interestFreePeriodTypeOptions = this.depositsDropdownReadPlatformService + .retrieveInterestFreePeriodFrequencyTypeOptions(); + + // charges + final boolean feeChargesOnly = true; + Collection chargeOptions = this.chargeReadPlatformService.retrieveSavingsAccountApplicableCharges(feeChargesOnly); + chargeOptions = CollectionUtils.isEmpty(chargeOptions) ? null : chargeOptions; + + Collection penaltyOptions = this.chargeReadPlatformService.retrieveSavingsAccountApplicablePenalties(); + penaltyOptions = CollectionUtils.isEmpty(penaltyOptions) ? null : penaltyOptions; + + Collection recurringDepositTypeOptions = this.depositsDropdownReadPlatformService + .retrieveRecurringDepositTypeOptions(); + Collection recurringDepositFrequencyTypeOptions = this.depositsDropdownReadPlatformService + .retrieveRecurringDepositFrequencyTypeOptions(); + + final Collection depositTermTypeOptions = this.depositsDropdownReadPlatformService.retrieveDepositTermTypeOptions(); + + final Collection inMultiplesOfDepositTermTypeOptions = this.depositsDropdownReadPlatformService + .retrieveInMultiplesOfDepositTermTypeOptions(); + + // interest rate chart template + final InterestRateChartData chartTemplate = this.interestRateChartReadPlatformService.template(); + + RecurringDepositProductData recurringDepositProductToReturn = null; + if (savingsProduct != null) { + recurringDepositProductToReturn = RecurringDepositProductData.withTemplate(savingsProduct, currencyOptions, + interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, + paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, chargeOptions, penaltyOptions, chartTemplate, + interestFreePeriodTypeOptions, preClosurePenalInterestOnTypeOptions, recurringDepositTypeOptions, + recurringDepositFrequencyTypeOptions, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions); + } else { + recurringDepositProductToReturn = RecurringDepositProductData.template(currency, interestCompoundingPeriodType, + interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, accountingRule, currencyOptions, + interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, + paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, chargeOptions, penaltyOptions, chartTemplate, + interestFreePeriodTypeOptions, preClosurePenalInterestOnTypeOptions, recurringDepositTypeOptions, + recurringDepositFrequencyTypeOptions, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions); + } + + return recurringDepositProductToReturn; + } + + @DELETE + @Path("{productId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String delete(@PathParam("productId") final Long productId) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteRecurringDepositProduct(productId).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountTransactionsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountTransactionsApiResource.java index d4d5cca96..db275af1e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountTransactionsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountTransactionsApiResource.java @@ -31,6 +31,7 @@ import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSeriali import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants; +import org.mifosplatform.portfolio.savings.DepositAccountType; import org.mifosplatform.portfolio.savings.SavingsApiConstants; import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionData; import org.mifosplatform.portfolio.savings.service.SavingsAccountReadPlatformService; @@ -81,7 +82,7 @@ public class SavingsAccountTransactionsApiResource { // FIXME - KW - for now just send back generic default information for // both deposit/withdrawal templates - SavingsAccountTransactionData savingsAccount = this.savingsAccountReadPlatformService.retrieveDepositTransactionTemplate(savingsId); + SavingsAccountTransactionData savingsAccount = this.savingsAccountReadPlatformService.retrieveDepositTransactionTemplate(savingsId, DepositAccountType.SAVINGS_DEPOSIT); final Collection paymentTypeOptions = this.codeValueReadPlatformService .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); savingsAccount = SavingsAccountTransactionData.templateOnTop(savingsAccount, paymentTypeOptions); @@ -100,7 +101,7 @@ public class SavingsAccountTransactionsApiResource { this.context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME); SavingsAccountTransactionData transactionData = this.savingsAccountReadPlatformService.retrieveSavingsTransaction(savingsId, - transactionId); + transactionId, DepositAccountType.SAVINGS_DEPOSIT); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); if (settings.isTemplate()) { final Collection paymentTypeOptions = this.codeValueReadPlatformService diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountsApiResource.java index 65645b090..e5e2d4441 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountsApiResource.java @@ -37,6 +37,7 @@ import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSeria import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.portfolio.group.service.SearchParameters; +import org.mifosplatform.portfolio.savings.DepositAccountType; import org.mifosplatform.portfolio.savings.SavingsApiConstants; import org.mifosplatform.portfolio.savings.data.SavingsAccountChargeData; import org.mifosplatform.portfolio.savings.data.SavingsAccountData; @@ -161,7 +162,7 @@ public class SavingsAccountsApiResource { if (associationParameters.contains(SavingsApiConstants.transactions)) { mandatoryResponseParameters.add(SavingsApiConstants.transactions); final Collection currentTransactions = this.savingsAccountReadPlatformService - .retrieveAllTransactions(accountId); + .retrieveAllTransactions(accountId, DepositAccountType.SAVINGS_DEPOSIT); if (!CollectionUtils.isEmpty(currentTransactions)) { transactions = currentTransactions; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountData.java new file mode 100644 index 000000000..c64a7091c --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountData.java @@ -0,0 +1,280 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.data; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.HashSet; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.organisation.monetary.data.CurrencyData; +import org.mifosplatform.organisation.staff.data.StaffData; +import org.mifosplatform.portfolio.charge.data.ChargeData; + +/** + * Immutable data object representing abstract for Fixed and Recurring Deposit Accounts + * Accounts. + */ +public class DepositAccountData { + + protected final Long id; + protected final String accountNo; + protected final String externalId; + protected final Long groupId; + protected final String groupName; + protected final Long clientId; + protected final String clientName; + protected final Long savingsProductId; + protected final String savingsProductName; + protected final Long fieldOfficerId; + protected final String fieldOfficerName; + protected final SavingsAccountStatusEnumData status; + protected final SavingsAccountApplicationTimelineData timeline; + protected final CurrencyData currency; + protected final BigDecimal nominalAnnualInterestRate; + protected final EnumOptionData interestCompoundingPeriodType; + protected final EnumOptionData interestPostingPeriodType; + protected final EnumOptionData interestCalculationType; + protected final EnumOptionData interestCalculationDaysInYearType; + protected final BigDecimal minRequiredOpeningBalance; + protected final Integer lockinPeriodFrequency; + protected final EnumOptionData lockinPeriodFrequencyType; + protected final boolean withdrawalFeeForTransfers; + protected final EnumOptionData depositType; + + // associations + protected final SavingsAccountSummaryData summary; + protected final Collection transactions; + + protected final Collection charges; + protected final DepositAccountInterestRateChartData accountChart; + + // template + protected final Collection productOptions; + protected final Collection fieldOfficerOptions; + protected final Collection interestCompoundingPeriodTypeOptions; + protected final Collection interestPostingPeriodTypeOptions; + protected final Collection interestCalculationTypeOptions; + protected final Collection interestCalculationDaysInYearTypeOptions; + protected final Collection lockinPeriodFrequencyTypeOptions; + protected final Collection withdrawalFeeTypeOptions; + protected final Collection chargeOptions; + + protected final SavingsAccountChargeData withdrawalFee; + protected final SavingsAccountChargeData annualFee; + + protected final DepositAccountInterestRateChartData chartTemplate; + + public static DepositAccountData instance(final Long id, final String accountNo, final String externalId, final Long groupId, + final String groupName, final Long clientId, final String clientName, final Long productId, final String productName, + final Long fieldOfficerId, final String fieldOfficerName, final SavingsAccountStatusEnumData status, + final SavingsAccountApplicationTimelineData timeline, final CurrencyData currency, final BigDecimal interestRate, + final EnumOptionData interestCompoundingPeriodType, final EnumOptionData interestPostingPeriodType, + final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType, + final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, + final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers, + final SavingsAccountSummaryData summary, final EnumOptionData depositType) { + + final Collection productOptions = null; + final Collection fieldOfficerOptions = null; + final Collection interestCompoundingPeriodTypeOptions = null; + final Collection interestPostingPeriodTypeOptions = null; + final Collection interestCalculationTypeOptions = null; + final Collection interestCalculationDaysInYearTypeOptions = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + + final Collection transactions = null; + final Collection charges = null; + final Collection chargeOptions = null; + final DepositAccountInterestRateChartData accountChart = null; + final DepositAccountInterestRateChartData chartTemplate = null; + + return new DepositAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, + fieldOfficerId, fieldOfficerName, status, timeline, currency, interestRate, interestCompoundingPeriodType, + interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions, + fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, + interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, + withdrawalFeeTypeOptions, charges, chargeOptions, accountChart, chartTemplate, depositType); + } + + public static DepositAccountData lookup(final Long id, final String accountNo, final EnumOptionData depositType) { + + final String externalId = null; + final Long groupId = null; + final String groupName = null; + final Long clientId = null; + final String clientName = null; + final Long productId = null; + final String productName = null; + final Long fieldOfficerId = null; + final String fieldOfficerName = null; + final SavingsAccountStatusEnumData status = null; + final SavingsAccountApplicationTimelineData timeline = null; + final CurrencyData currency = null; + final BigDecimal interestRate = null; + final EnumOptionData interestCompoundingPeriodType = null; + final EnumOptionData interestPostingPeriodType = null; + final EnumOptionData interestCalculationType = null; + final EnumOptionData interestCalculationDaysInYearType = null; + final BigDecimal minRequiredOpeningBalance = null; + final Integer lockinPeriodFrequency = null; + final EnumOptionData lockinPeriodFrequencyType = null; + final boolean withdrawalFeeForTransfers = false; + final SavingsAccountSummaryData summary = null; + final Collection productOptions = null; + final Collection fieldOfficerOptions = null; + final Collection interestCompoundingPeriodTypeOptions = null; + final Collection interestPostingPeriodTypeOptions = null; + final Collection interestCalculationTypeOptions = null; + final Collection interestCalculationDaysInYearTypeOptions = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + + final Collection transactions = null; + final Collection charges = null; + final Collection chargeOptions = null; + final DepositAccountInterestRateChartData accountChart = null; + final DepositAccountInterestRateChartData chartTemplate = null; + + return new DepositAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, + fieldOfficerId, fieldOfficerName, status, timeline, currency, interestRate, interestCompoundingPeriodType, + interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions, + fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, + interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, + withdrawalFeeTypeOptions, charges, chargeOptions, accountChart, chartTemplate, depositType); + } + + protected DepositAccountData(final Long id, final String accountNo, final String externalId, final Long groupId, + final String groupName, final Long clientId, final String clientName, final Long productId, final String productName, + final Long fieldofficerId, final String fieldofficerName, final SavingsAccountStatusEnumData status, + final SavingsAccountApplicationTimelineData timeline, final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, + final EnumOptionData interestPeriodType, final EnumOptionData interestPostingPeriodType, + final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType, + final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, + final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers, + final SavingsAccountSummaryData summary, final Collection transactions, + final Collection productOptions, final Collection fieldOfficerOptions, + final Collection interestCompoundingPeriodTypeOptions, + final Collection interestPostingPeriodTypeOptions, + final Collection interestCalculationTypeOptions, + final Collection interestCalculationDaysInYearTypeOptions, + final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, + final Collection charges, final Collection chargeOptions, + final DepositAccountInterestRateChartData accountChart, final DepositAccountInterestRateChartData chartTemplate, + final EnumOptionData depositType) { + this.id = id; + this.accountNo = accountNo; + this.externalId = externalId; + this.groupId = groupId; + this.groupName = groupName; + this.clientId = clientId; + this.clientName = clientName; + this.savingsProductId = productId; + this.savingsProductName = productName; + this.fieldOfficerId = fieldofficerId; + this.fieldOfficerName = fieldofficerName; + this.status = status; + this.timeline = timeline; + this.currency = currency; + this.nominalAnnualInterestRate = nominalAnnualInterestRate; + this.interestCompoundingPeriodType = interestPeriodType; + this.interestPostingPeriodType = interestPostingPeriodType; + this.interestCalculationType = interestCalculationType; + this.interestCalculationDaysInYearType = interestCalculationDaysInYearType; + this.minRequiredOpeningBalance = minRequiredOpeningBalance; + this.lockinPeriodFrequency = lockinPeriodFrequency; + this.lockinPeriodFrequencyType = lockinPeriodFrequencyType; + this.withdrawalFeeForTransfers = withdrawalFeeForTransfers; + this.summary = summary; + this.transactions = transactions; + this.productOptions = productOptions; + this.fieldOfficerOptions = fieldOfficerOptions; + this.interestCompoundingPeriodTypeOptions = interestCompoundingPeriodTypeOptions; + this.interestPostingPeriodTypeOptions = interestPostingPeriodTypeOptions; + this.interestCalculationTypeOptions = interestCalculationTypeOptions; + this.interestCalculationDaysInYearTypeOptions = interestCalculationDaysInYearTypeOptions; + this.lockinPeriodFrequencyTypeOptions = lockinPeriodFrequencyTypeOptions; + this.withdrawalFeeTypeOptions = withdrawalFeeTypeOptions; + + this.charges = charges;// charges associated with Savings account + // charges available for adding to Savings account + this.chargeOptions = chargeOptions; + + this.withdrawalFee = getWithdrawalFee(); + + this.annualFee = getAnnualFee(); + + this.accountChart = accountChart; + this.chartTemplate = chartTemplate; + this.depositType = depositType; + } + + private SavingsAccountChargeData getWithdrawalFee() { + for (SavingsAccountChargeData charge : this.charges()) { + if (charge.isWithdrawalFee()) return charge; + } + return null; + } + + private SavingsAccountChargeData getAnnualFee() { + for (SavingsAccountChargeData charge : this.charges()) { + if (charge.isAnnualFee()) return charge; + } + return null; + } + + public Long id() { + return this.id; + } + + public Long clientId() { + return this.clientId; + } + + public Long groupId() { + return this.groupId; + } + + public Long productId() { + return this.savingsProductId; + } + + public CurrencyData currency() { + return this.currency; + } + + @Override + public boolean equals(final Object obj) { + + if (obj == null) { return false; } + if (obj == this) { return true; } + if (obj.getClass() != getClass()) { return false; } + final DepositAccountData rhs = (DepositAccountData) obj; + return new EqualsBuilder().append(this.id, rhs.id).append(this.accountNo, rhs.accountNo).isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37).append(this.id).append(this.accountNo).toHashCode(); + } + + public Collection charges() { + return (this.charges == null) ? new HashSet() : this.charges; + } + + public EnumOptionData depositType() { + return depositType; + } + + public String accountNo() { + return accountNo; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountDataValidator.java new file mode 100644 index 000000000..9fe0c46b0 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountDataValidator.java @@ -0,0 +1,613 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.data; + +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.minDepositTermTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.maxDepositTermTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositAmountParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositPeriodParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositPeriodFrequencyIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.inMultiplesOfDepositTermParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.inMultiplesOfDepositTermTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.interestFreeFromPeriodParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.interestFreePeriodApplicableParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.interestFreePeriodFrequencyTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.interestFreeToPeriodParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.maxDepositTermParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.minDepositTermParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.preClosurePenalApplicableParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.preClosurePenalInterestOnTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.preClosurePenalInterestParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositFrequencyParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositFrequencyTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositTypeIdParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountNoParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.amountParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeIdParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargesParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.clientIdParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.externalIdParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeIntervalParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeOnMonthDayParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.fieldOfficerIdParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.groupIdParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.productIdParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.submittedOnDateParamName; + +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; +import org.joda.time.MonthDay; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.portfolio.interestratechart.InterestRateChartPeriodType; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.DepositsApiConstants; +import org.mifosplatform.portfolio.savings.PreClosurePenalInterestOnType; +import org.mifosplatform.portfolio.savings.RecurringDepositType; +import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; +import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; +import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; + +@Component +public class DepositAccountDataValidator { + + private final FromJsonHelper fromApiJsonHelper; + private final DepositProductDataValidator productDataValidator; + + @Autowired + public DepositAccountDataValidator(final FromJsonHelper fromApiJsonHelper, final DepositProductDataValidator productDataValidator) { + this.fromApiJsonHelper = fromApiJsonHelper; + this.productDataValidator = productDataValidator; + } + + private void validateFixedDepositForSubmit(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, + DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME); + final JsonElement element = this.fromApiJsonHelper.parse(json); + + validateDepositDetailsForSubmit(element, baseDataValidator); + validatePreClosureDetailForSubmit(element, baseDataValidator); + validateDepositTermDeatilForSubmit(element, baseDataValidator, DepositAccountType.FIXED_DEPOSIT); + validateSavingsCharges(element, baseDataValidator); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + private void validateFixedDepositForUpdate(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, + DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME); + final JsonElement element = this.fromApiJsonHelper.parse(json); + + validateDepositDetailsForUpdate(element, baseDataValidator); + validatePreClosureDetailForUpdate(element, baseDataValidator); + validateDepositTermDeatilForUpdate(element, baseDataValidator); + // validateSavingsCharges(element, baseDataValidator); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + private void validateRecurringDepositForSubmit(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, + DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME); + final JsonElement element = this.fromApiJsonHelper.parse(json); + + validateDepositDetailsForSubmit(element, baseDataValidator); + validatePreClosureDetailForSubmit(element, baseDataValidator); + validateDepositTermDeatilForSubmit(element, baseDataValidator, DepositAccountType.RECURRING_DEPOSIT); + validateRecurringDetailForSubmit(element, baseDataValidator); + validateSavingsCharges(element, baseDataValidator); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + private void validateRecurringDepositForUpdate(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, + DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME); + final JsonElement element = this.fromApiJsonHelper.parse(json); + + validateDepositDetailsForUpdate(element, baseDataValidator); + validatePreClosureDetailForUpdate(element, baseDataValidator); + validateDepositTermDeatilForUpdate(element, baseDataValidator); + validateRecurringDetailForUpdate(element, baseDataValidator); + // validateSavingsCharges(element, baseDataValidator); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + + } + + private void validateDepositDetailsForSubmit(final JsonElement element, final DataValidatorBuilder baseDataValidator) { + + final Long clientId = this.fromApiJsonHelper.extractLongNamed(clientIdParamName, element); + if (clientId != null) { + baseDataValidator.reset().parameter(clientIdParamName).value(clientId).longGreaterThanZero(); + } + + final Long groupId = this.fromApiJsonHelper.extractLongNamed(groupIdParamName, element); + if (groupId != null) { + baseDataValidator.reset().parameter(groupIdParamName).value(groupId).longGreaterThanZero(); + } + + if (clientId == null && groupId == null) { + baseDataValidator.reset().parameter(clientIdParamName).value(clientId).notNull().integerGreaterThanZero(); + } + + final Long productId = this.fromApiJsonHelper.extractLongNamed(productIdParamName, element); + baseDataValidator.reset().parameter(productIdParamName).value(productId).notNull().integerGreaterThanZero(); + + if (this.fromApiJsonHelper.parameterExists(fieldOfficerIdParamName, element)) { + final Long fieldOfficerId = this.fromApiJsonHelper.extractLongNamed(fieldOfficerIdParamName, element); + baseDataValidator.reset().parameter(fieldOfficerIdParamName).value(fieldOfficerId).ignoreIfNull().integerGreaterThanZero(); + } + + final LocalDate submittedOnDate = this.fromApiJsonHelper.extractLocalDateNamed(submittedOnDateParamName, element); + baseDataValidator.reset().parameter(submittedOnDateParamName).value(submittedOnDate).notNull(); + + if (this.fromApiJsonHelper.parameterExists(accountNoParamName, element)) { + final String accountNo = this.fromApiJsonHelper.extractStringNamed(accountNoParamName, element); + baseDataValidator.reset().parameter(accountNoParamName).value(accountNo).notBlank().notExceedingLengthOf(20); + } + + if (this.fromApiJsonHelper.parameterExists(externalIdParamName, element)) { + final String externalId = this.fromApiJsonHelper.extractStringNamed(externalIdParamName, element); + baseDataValidator.reset().parameter(externalIdParamName).value(externalId).notExceedingLengthOf(100); + } + + if (this.fromApiJsonHelper.parameterExists(nominalAnnualInterestRateParamName, element)) { + final BigDecimal interestRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(nominalAnnualInterestRateParamName, + element); + baseDataValidator.reset().parameter(nominalAnnualInterestRateParamName).value(interestRate).notNull().zeroOrPositiveAmount(); + } + + if (this.fromApiJsonHelper.parameterExists(interestCompoundingPeriodTypeParamName, element)) { + final Integer interestCompoundingPeriodType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed( + interestCompoundingPeriodTypeParamName, element); + baseDataValidator.reset().parameter(interestCompoundingPeriodTypeParamName).value(interestCompoundingPeriodType).notNull() + .isOneOfTheseValues(SavingsCompoundingInterestPeriodType.integerValues()); + } + + if (this.fromApiJsonHelper.parameterExists(interestPostingPeriodTypeParamName, element)) { + final Integer interestPostingPeriodType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed( + interestPostingPeriodTypeParamName, element); + baseDataValidator.reset().parameter(interestPostingPeriodTypeParamName).value(interestPostingPeriodType).notNull() + .isOneOfTheseValues(SavingsPostingInterestPeriodType.integerValues()); + } + + if (this.fromApiJsonHelper.parameterExists(interestCalculationTypeParamName, element)) { + final Integer interestCalculationType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(interestCalculationTypeParamName, + element); + baseDataValidator.reset().parameter(interestCalculationTypeParamName).value(interestCalculationType).notNull() + .isOneOfTheseValues(SavingsInterestCalculationType.integerValues()); + } + + if (this.fromApiJsonHelper.parameterExists(interestCalculationDaysInYearTypeParamName, element)) { + final Integer interestCalculationDaysInYearType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed( + interestCalculationDaysInYearTypeParamName, element); + baseDataValidator.reset().parameter(interestCalculationDaysInYearTypeParamName).value(interestCalculationDaysInYearType) + .notNull().isOneOfTheseValues(SavingsInterestCalculationDaysInYearType.integerValues()); + } + + if (this.fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) { + final BigDecimal minOpeningBalance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed( + minRequiredOpeningBalanceParamName, element); + baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).zeroOrPositiveAmount(); + } + + if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) { + + final Integer lockinPeriodFrequency = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, + element); + baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).integerZeroOrGreater(); + + if (lockinPeriodFrequency != null) { + final Integer lockinPeriodFrequencyType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed( + lockinPeriodFrequencyTypeParamName, element); + baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).notNull() + .inMinMaxRange(0, 3); + } + } + + if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyTypeParamName, element)) { + final Integer lockinPeriodFrequencyType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed( + lockinPeriodFrequencyTypeParamName, element); + baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).inMinMaxRange(0, 3); + + if (lockinPeriodFrequencyType != null) { + final Integer lockinPeriodFrequency = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, + element); + baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).notNull() + .integerZeroOrGreater(); + } + } + } + + private void validateDepositDetailsForUpdate(final JsonElement element, final DataValidatorBuilder baseDataValidator) { + Long clientId = null; + if (this.fromApiJsonHelper.parameterExists(clientIdParamName, element)) { + clientId = this.fromApiJsonHelper.extractLongNamed(clientIdParamName, element); + baseDataValidator.reset().parameter(clientIdParamName).value(clientId).ignoreIfNull().longGreaterThanZero(); + + Long groupId = null; + if (this.fromApiJsonHelper.parameterExists(productIdParamName, element)) { + groupId = this.fromApiJsonHelper.extractLongNamed(groupIdParamName, element); + baseDataValidator.reset().parameter(groupIdParamName).value(groupId).ignoreIfNull().longGreaterThanZero(); + } + + if (clientId == null && groupId == null) { + // either clientId or groupId must exists if param passed for + // update. + baseDataValidator.reset().parameter(clientIdParamName).value(clientId).notNull().integerGreaterThanZero(); + } + } + + Long groupId = null; + if (this.fromApiJsonHelper.parameterExists(groupIdParamName, element)) { + groupId = this.fromApiJsonHelper.extractLongNamed(groupIdParamName, element); + baseDataValidator.reset().parameter(groupIdParamName).value(groupId).ignoreIfNull().longGreaterThanZero(); + + if (this.fromApiJsonHelper.parameterExists(clientIdParamName, element)) { + clientId = this.fromApiJsonHelper.extractLongNamed(clientIdParamName, element); + baseDataValidator.reset().parameter(clientIdParamName).value(clientId).ignoreIfNull().longGreaterThanZero(); + } + + if (clientId == null && groupId == null) { + // either clientId or groupId must exists if param passed for + // update. + baseDataValidator.reset().parameter(clientIdParamName).value(clientId).notNull().integerGreaterThanZero(); + } + } + + if (this.fromApiJsonHelper.parameterExists(productIdParamName, element)) { + final Long productId = this.fromApiJsonHelper.extractLongNamed(productIdParamName, element); + baseDataValidator.reset().parameter(productIdParamName).value(productId).notNull().integerGreaterThanZero(); + } + + if (this.fromApiJsonHelper.parameterExists(fieldOfficerIdParamName, element)) { + final Long fieldOfficerId = this.fromApiJsonHelper.extractLongNamed(fieldOfficerIdParamName, element); + baseDataValidator.reset().parameter(fieldOfficerIdParamName).value(fieldOfficerId).ignoreIfNull().integerGreaterThanZero(); + } + + if (this.fromApiJsonHelper.parameterExists(submittedOnDateParamName, element)) { + final LocalDate submittedOnDate = this.fromApiJsonHelper.extractLocalDateNamed(submittedOnDateParamName, element); + baseDataValidator.reset().parameter(submittedOnDateParamName).value(submittedOnDate).notNull(); + } + + if (this.fromApiJsonHelper.parameterExists(accountNoParamName, element)) { + final String accountNo = this.fromApiJsonHelper.extractStringNamed(accountNoParamName, element); + baseDataValidator.reset().parameter(accountNoParamName).value(accountNo).notBlank().notExceedingLengthOf(20); + } + + if (this.fromApiJsonHelper.parameterExists(externalIdParamName, element)) { + final String externalId = this.fromApiJsonHelper.extractStringNamed(externalIdParamName, element); + baseDataValidator.reset().parameter(externalIdParamName).value(externalId).notExceedingLengthOf(100); + } + + if (this.fromApiJsonHelper.parameterExists(nominalAnnualInterestRateParamName, element)) { + final BigDecimal interestRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(nominalAnnualInterestRateParamName, + element); + baseDataValidator.reset().parameter(nominalAnnualInterestRateParamName).value(interestRate).notNull().zeroOrPositiveAmount(); + } + + if (this.fromApiJsonHelper.parameterExists(interestCompoundingPeriodTypeParamName, element)) { + final Integer interestCompoundingPeriodType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed( + interestCompoundingPeriodTypeParamName, element); + baseDataValidator.reset().parameter(interestCompoundingPeriodTypeParamName).value(interestCompoundingPeriodType).notNull() + .isOneOfTheseValues(SavingsCompoundingInterestPeriodType.integerValues()); + } + + if (this.fromApiJsonHelper.parameterExists(interestPostingPeriodTypeParamName, element)) { + final Integer interestPostingPeriodType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed( + interestPostingPeriodTypeParamName, element); + baseDataValidator.reset().parameter(interestPostingPeriodTypeParamName).value(interestPostingPeriodType).notNull() + .isOneOfTheseValues(SavingsPostingInterestPeriodType.integerValues()); + } + + if (this.fromApiJsonHelper.parameterExists(interestCalculationTypeParamName, element)) { + final Integer interestCalculationType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(interestCalculationTypeParamName, + element); + baseDataValidator.reset().parameter(interestCalculationTypeParamName).value(interestCalculationType).notNull() + .isOneOfTheseValues(SavingsInterestCalculationType.integerValues()); + } + + if (this.fromApiJsonHelper.parameterExists(interestCalculationDaysInYearTypeParamName, element)) { + final Integer interestCalculationDaysInYearType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed( + interestCalculationDaysInYearTypeParamName, element); + baseDataValidator.reset().parameter(interestCalculationDaysInYearTypeParamName).value(interestCalculationDaysInYearType) + .notNull().isOneOfTheseValues(SavingsInterestCalculationDaysInYearType.integerValues()); + } + + if (this.fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) { + final BigDecimal minOpeningBalance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed( + minRequiredOpeningBalanceParamName, element); + baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).ignoreIfNull() + .zeroOrPositiveAmount(); + } + + if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) { + final Integer lockinPeriodFrequency = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, + element); + baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).ignoreIfNull() + .integerZeroOrGreater(); + } + + if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyTypeParamName, element)) { + final Integer lockinPeriodFrequencyType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed( + lockinPeriodFrequencyTypeParamName, element); + baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).inMinMaxRange(0, 3); + } + + } + + private void validatePreClosureDetailForSubmit(final JsonElement element, final DataValidatorBuilder baseDataValidator) { + if (fromApiJsonHelper.parameterExists(interestFreePeriodApplicableParamName, element)) { + + final boolean interestFreePeriodApplicable = fromApiJsonHelper.extractBooleanNamed(interestFreePeriodApplicableParamName, + element); + + if (interestFreePeriodApplicable) { + final Integer interestFreeFromPeriod = fromApiJsonHelper.extractIntegerSansLocaleNamed(interestFreeFromPeriodParamName, + element); + baseDataValidator.reset().parameter(interestFreeFromPeriodParamName).value(interestFreeFromPeriod) + .cantBeBlankWhenParameterProvidedIs(interestFreePeriodApplicableParamName, interestFreePeriodApplicable) + .integerZeroOrGreater(); + + final Integer interestFreeToPeriod = fromApiJsonHelper + .extractIntegerSansLocaleNamed(interestFreeToPeriodParamName, element); + baseDataValidator.reset().parameter(interestFreeToPeriodParamName).value(interestFreeToPeriod) + .cantBeBlankWhenParameterProvidedIs(interestFreePeriodApplicableParamName, interestFreePeriodApplicable) + .integerZeroOrGreater().integerGreaterThanNumber(interestFreeFromPeriod); + + final Integer periodFrequencyType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed( + interestFreePeriodFrequencyTypeIdParamName, element); + baseDataValidator.reset().parameter(interestFreePeriodFrequencyTypeIdParamName).value(periodFrequencyType) + .cantBeBlankWhenParameterProvidedIs(interestFreePeriodApplicableParamName, interestFreePeriodApplicable) + .isOneOfTheseValues(InterestRateChartPeriodType.integerValues()); + } + } + + if (fromApiJsonHelper.parameterExists(preClosurePenalApplicableParamName, element)) { + final boolean preClosurePenalApplicable = fromApiJsonHelper.extractBooleanNamed(preClosurePenalApplicableParamName, element); + + if (preClosurePenalApplicable) { + final BigDecimal penalInterestRate = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(preClosurePenalInterestParamName, + element); + baseDataValidator.reset().parameter(preClosurePenalInterestParamName).value(penalInterestRate) + .cantBeBlankWhenParameterProvidedIs(preClosurePenalApplicableParamName, preClosurePenalApplicable) + .zeroOrPositiveAmount(); + + final Integer preClosurePenalInterestType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed( + preClosurePenalInterestOnTypeIdParamName, element); + baseDataValidator.reset().parameter(preClosurePenalInterestOnTypeIdParamName).value(preClosurePenalInterestType) + .cantBeBlankWhenParameterProvidedIs(preClosurePenalApplicableParamName, preClosurePenalApplicable) + .isOneOfTheseValues(PreClosurePenalInterestOnType.integerValues()); + } + } + } + + private void validatePreClosureDetailForUpdate(final JsonElement element, final DataValidatorBuilder baseDataValidator) { + this.productDataValidator.validatePreClosureDetailForUpdate(element, baseDataValidator); + } + + private void validateDepositTermDeatilForSubmit(final JsonElement element, final DataValidatorBuilder baseDataValidator, + final DepositAccountType depositType) { + + Integer minTerm = null; + if (fromApiJsonHelper.parameterExists(minDepositTermParamName, element)) { + minTerm = fromApiJsonHelper.extractIntegerSansLocaleNamed(minDepositTermParamName, element); + baseDataValidator.reset().parameter(minDepositTermParamName).value(minTerm).integerGreaterThanZero(); + } + + if (fromApiJsonHelper.parameterExists(maxDepositTermParamName, element)) { + final Integer maxTerm = fromApiJsonHelper.extractIntegerSansLocaleNamed(maxDepositTermParamName, element); + baseDataValidator.reset().parameter(maxDepositTermParamName).value(maxTerm).integerGreaterThanZero(); + } + + if (fromApiJsonHelper.parameterExists(minDepositTermTypeIdParamName, element)) { + final Integer minDepositTermType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(minDepositTermTypeIdParamName, element); + baseDataValidator.reset().parameter(minDepositTermTypeIdParamName).value(minDepositTermType) + .isOneOfTheseValues(SavingsPeriodFrequencyType.integerValues()); + } + + if (fromApiJsonHelper.parameterExists(maxDepositTermTypeIdParamName, element)) { + final Integer maxDepositTermType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(maxDepositTermTypeIdParamName, element); + baseDataValidator.reset().parameter(maxDepositTermTypeIdParamName).value(maxDepositTermType) + .isOneOfTheseValues(SavingsPeriodFrequencyType.integerValues()); + } + + if (fromApiJsonHelper.parameterExists(inMultiplesOfDepositTermParamName, element)) { + final Integer inMultiplesOfDepositTerm = fromApiJsonHelper.extractIntegerSansLocaleNamed(inMultiplesOfDepositTermParamName, + element); + baseDataValidator.reset().parameter(inMultiplesOfDepositTermParamName).value(inMultiplesOfDepositTerm).integerGreaterThanZero(); + final Integer inMultiplesOfDepositTermType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed( + inMultiplesOfDepositTermTypeIdParamName, element); + baseDataValidator.reset().parameter(inMultiplesOfDepositTermTypeIdParamName).value(inMultiplesOfDepositTermType) + .cantBeBlankWhenParameterProvidedIs(inMultiplesOfDepositTermParamName, inMultiplesOfDepositTerm) + .isOneOfTheseValues(SavingsPeriodFrequencyType.integerValues()); + } + + // for recurring deposit the total deposit amount is derived from + // recurring deposit amount * number of deposits. + if (depositType.isFixedDeposit()) { + final BigDecimal depositAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(depositAmountParamName, element); + baseDataValidator.reset().parameter(depositAmountParamName).value(depositAmount).notNull().positiveAmount(); + } + + final Integer depositPeriod = fromApiJsonHelper.extractIntegerSansLocaleNamed(depositPeriodParamName, element); + baseDataValidator.reset().parameter(depositPeriodParamName).value(depositPeriod).notNull().integerGreaterThanZero(); + + final Integer depositPeriodFrequencyId = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(depositPeriodFrequencyIdParamName, + element); + baseDataValidator.reset().parameter(depositPeriodFrequencyIdParamName).value(depositPeriodFrequencyId) + .isOneOfTheseValues(SavingsPeriodFrequencyType.integerValues()); + } + + private void validateDepositTermDeatilForUpdate(final JsonElement element, final DataValidatorBuilder baseDataValidator) { + this.productDataValidator.validateDepositTermDetailForUpdate(element, baseDataValidator); + + if (fromApiJsonHelper.parameterExists(depositAmountParamName, element)) { + final BigDecimal depositAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(depositAmountParamName, element); + baseDataValidator.reset().parameter(depositAmountParamName).value(depositAmount).notNull().positiveAmount(); + } + + if (fromApiJsonHelper.parameterExists(depositPeriodParamName, element)) { + final Integer depositPeriod = fromApiJsonHelper.extractIntegerSansLocaleNamed(depositPeriodParamName, element); + baseDataValidator.reset().parameter(depositPeriodParamName).value(depositPeriod).notNull().integerGreaterThanZero(); + } + + if (fromApiJsonHelper.parameterExists(depositPeriodFrequencyIdParamName, element)) { + final Integer depositPeriodFrequencyId = this.fromApiJsonHelper.extractIntegerSansLocaleNamed( + depositPeriodFrequencyIdParamName, element); + baseDataValidator.reset().parameter(depositPeriodFrequencyIdParamName).value(depositPeriodFrequencyId) + .isOneOfTheseValues(SavingsPeriodFrequencyType.integerValues()); + } + } + + private void validateRecurringDetailForSubmit(final JsonElement element, final DataValidatorBuilder baseDataValidator) { + if (fromApiJsonHelper.parameterExists(recurringDepositTypeIdParamName, element)) { + final Integer rdTypeId = fromApiJsonHelper.extractIntegerSansLocaleNamed(recurringDepositTypeIdParamName, element); + baseDataValidator.reset().parameter(recurringDepositTypeIdParamName).value(rdTypeId).notNull() + .isOneOfTheseValues(RecurringDepositType.integerValues()); + } + + if (fromApiJsonHelper.parameterExists(recurringDepositFrequencyParamName, element)) { + final Integer rdFrequency = fromApiJsonHelper.extractIntegerSansLocaleNamed(recurringDepositFrequencyParamName, element); + baseDataValidator.reset().parameter(recurringDepositFrequencyParamName).value(rdFrequency).notNull().integerGreaterThanZero(); + } + + if (fromApiJsonHelper.parameterExists(recurringDepositFrequencyTypeIdParamName, element)) { + final Integer rdFrequencyTypeId = fromApiJsonHelper.extractIntegerSansLocaleNamed(recurringDepositFrequencyTypeIdParamName, + element); + baseDataValidator.reset().parameter(recurringDepositFrequencyTypeIdParamName).value(rdFrequencyTypeId).notNull() + .isOneOfTheseValues(SavingsPeriodFrequencyType.integerValues()); + } + } + + private void validateRecurringDetailForUpdate(final JsonElement element, final DataValidatorBuilder baseDataValidator) { + this.productDataValidator.validateRecurringDepositUpdate(element, baseDataValidator); + } + + public void validateForSubmit(final String json, final DepositAccountType depositAccountType) { + + if (depositAccountType.isFixedDeposit()) { + this.validateFixedDepositForSubmit(json); + } else if (depositAccountType.isRecurringDeposit()) { + this.validateRecurringDepositForSubmit(json); + } else { + // FIXME: AA handle unsupported deposit account type + // throw unsupported account type exception + } + } + + private void validateSavingsCharges(final JsonElement element, final DataValidatorBuilder baseDataValidator) { + + if (element.isJsonObject()) { + final JsonObject topLevelJsonElement = element.getAsJsonObject(); + final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement); + final String monthDayFormat = this.fromApiJsonHelper.extractMonthDayFormatParameter(topLevelJsonElement); + if (topLevelJsonElement.has(chargesParamName) && topLevelJsonElement.get(chargesParamName).isJsonArray()) { + final JsonArray array = topLevelJsonElement.get(chargesParamName).getAsJsonArray(); + for (int i = 0; i < array.size(); i++) { + + final JsonObject savingsChargeElement = array.get(i).getAsJsonObject(); + + // final Long id = + // this.fromApiJsonHelper.extractLongNamed(idParamName, + // savingsChargeElement); + + final Long chargeId = this.fromApiJsonHelper.extractLongNamed(chargeIdParamName, savingsChargeElement); + baseDataValidator.reset().parameter(chargeIdParamName).value(chargeId).longGreaterThanZero(); + + final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalNamed(amountParamName, savingsChargeElement, locale); + baseDataValidator.reset().parameter(amountParamName).value(amount).notNull().positiveAmount(); + + if (this.fromApiJsonHelper.parameterExists(feeOnMonthDayParamName, savingsChargeElement)) { + final MonthDay monthDay = this.fromApiJsonHelper.extractMonthDayNamed(feeOnMonthDayParamName, savingsChargeElement, + monthDayFormat, locale); + baseDataValidator.reset().parameter(feeOnMonthDayParamName).value(monthDay).notNull(); + } + + if (this.fromApiJsonHelper.parameterExists(feeIntervalParamName, savingsChargeElement)) { + final Integer feeInterval = this.fromApiJsonHelper.extractIntegerNamed(feeIntervalParamName, savingsChargeElement, + Locale.getDefault()); + baseDataValidator.reset().parameter(feeIntervalParamName).value(feeInterval).notNull().inMinMaxRange(1, 12); + } + } + } + } + } + + public void validateForUpdate(final String json, final DepositAccountType depositAccountType) { + + if (depositAccountType.isFixedDeposit()) { + this.validateFixedDepositForUpdate(json); + } else if (depositAccountType.isRecurringDeposit()) { + this.validateRecurringDepositForUpdate(json); + } else { + // FIXME: AA handle unsupported deposit account type + // throw unsupported account type exception + } + + } + + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountInterestRateChartData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountInterestRateChartData.java new file mode 100644 index 000000000..dca214c72 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountInterestRateChartData.java @@ -0,0 +1,120 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.data; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartData; +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartSlabData; + +/** + * Immutable data object representing a deposit account interest rate chart. + */ +public class DepositAccountInterestRateChartData { + + private final Long id; + private final String name; + private final String description; + private final LocalDate fromDate; + private final LocalDate endDate; + private final Long accountId; + private final String accountNumber; + // associations + private Set chartSlabs; + + // template + private Collection periodTypes; + + public static DepositAccountInterestRateChartData instance(Long id, String name, String description, LocalDate fromDate, + LocalDate endDate, Long accountId, String accountNumber, Set chartSlabs, + Collection periodTypes) { + + return new DepositAccountInterestRateChartData(id, name, description, fromDate, endDate, accountId, accountNumber, chartSlabs, + periodTypes); + } + + public static DepositAccountInterestRateChartData from(InterestRateChartData productChartData) { + final Long id = null; + final Long accountId = null; + final String accountNumber = null; + Set fromProdChartSlabs = new HashSet(); + Set productChartSlabDatas = productChartData.chartSlabs(); + if (productChartSlabDatas != null) { + for (InterestRateChartSlabData productChartSlabData : productChartSlabDatas) { + fromProdChartSlabs.add(DepositAccountInterestRateChartSlabData.from(productChartSlabData)); + } + } + + return new DepositAccountInterestRateChartData(id, productChartData.name(), productChartData.description(), + productChartData.fromDate(), productChartData.endDate(), accountId, accountNumber, fromProdChartSlabs, + productChartData.periodTypes()); + } + + public static DepositAccountInterestRateChartData withSlabs(DepositAccountInterestRateChartData interestRateChartData, + Set chartSlabs) { + return new DepositAccountInterestRateChartData(interestRateChartData.id, interestRateChartData.name, + interestRateChartData.description, interestRateChartData.fromDate, interestRateChartData.endDate, + interestRateChartData.accountId, interestRateChartData.accountNumber, chartSlabs, interestRateChartData.periodTypes); + } + + public static DepositAccountInterestRateChartData withTemplate(DepositAccountInterestRateChartData interestRateChartData, + Collection periodTypes) { + return new DepositAccountInterestRateChartData(interestRateChartData.id, interestRateChartData.name, + interestRateChartData.description, interestRateChartData.fromDate, interestRateChartData.endDate, + interestRateChartData.accountId, interestRateChartData.accountNumber, interestRateChartData.chartSlabs, periodTypes); + } + + public static DepositAccountInterestRateChartData template(Collection periodTypes) { + final Long id = null; + final String name = null; + final String description = null; + final LocalDate fromDate = null; + final LocalDate endDate = null; + final Long accountId = null; + final String accountNumber = null; + final Set chartSlabs = null; + return new DepositAccountInterestRateChartData(id, name, description, fromDate, endDate, accountId, accountNumber, chartSlabs, + periodTypes); + } + + private DepositAccountInterestRateChartData(Long id, String name, String description, LocalDate fromDate, LocalDate endDate, + Long accountId, String accountNumber, Set chartSlabs, + Collection periodTypes) { + this.id = id; + this.name = name; + this.description = description; + this.fromDate = fromDate; + this.endDate = endDate; + this.chartSlabs = chartSlabs; + this.accountId = accountId; + this.accountNumber = accountNumber; + this.periodTypes = periodTypes; + } + + public void addChartSlab(final DepositAccountInterestRateChartSlabData chartSlab) { + if (this.chartSlabs == null) { + this.chartSlabs = new HashSet(); + } + + this.chartSlabs.add(chartSlab); + } + + public boolean isFromDateAfter(final LocalDate compareDate) { + return (compareDate == null) ? false : this.fromDate.isAfter(compareDate); + } + + public LocalDate endDate() { + return this.endDate; + } + + public LocalDate fromDate() { + return this.fromDate; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountInterestRateChartSlabData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountInterestRateChartSlabData.java new file mode 100644 index 000000000..f199e88f0 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountInterestRateChartSlabData.java @@ -0,0 +1,68 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.data; + +import java.math.BigDecimal; +import java.util.Collection; + +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.organisation.monetary.data.CurrencyData; +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartSlabData; + +/** + * Immutable data object representing deposit accounts Interest rate Slabs. + */ +public class DepositAccountInterestRateChartSlabData { + + private final Long id; + private final String description; + private final EnumOptionData periodType; + private final Integer fromPeriod; + private final Integer toPeriod; + private final BigDecimal amountRangeFrom; + private final BigDecimal amountRangeTo; + private final BigDecimal annualInterestRate; + private final CurrencyData currency; + + // associations + + // template + @SuppressWarnings("unused") + private Collection periodTypes; + + public static DepositAccountInterestRateChartSlabData instance(Long id, String description, EnumOptionData periodType, Integer fromPeriod, + Integer toPeriod, BigDecimal amountRangeFrom, BigDecimal amountRangeTo, BigDecimal annualInterestRate, CurrencyData currency) { + Collection periodTypes = null; + return new DepositAccountInterestRateChartSlabData(id, description, periodType, fromPeriod, toPeriod, amountRangeFrom, amountRangeTo, + annualInterestRate, currency, periodTypes); + } + + public static DepositAccountInterestRateChartSlabData from(InterestRateChartSlabData chartSlabData) { + final Long id = null; + return new DepositAccountInterestRateChartSlabData(id, chartSlabData.description(), chartSlabData.periodType(), chartSlabData.fromPeriod(), chartSlabData.toPeriod(), chartSlabData.amountRangeFrom(), chartSlabData.amountRangeTo(), + chartSlabData.annualInterestRate(), chartSlabData.currency(), chartSlabData.periodTypes()); + } + + public static DepositAccountInterestRateChartSlabData withTemplate(DepositAccountInterestRateChartSlabData chartSlab, Collection periodTypes) { + return new DepositAccountInterestRateChartSlabData(chartSlab.id, chartSlab.description, chartSlab.periodType, chartSlab.fromPeriod, chartSlab.toPeriod, chartSlab.amountRangeFrom, chartSlab.amountRangeTo, + chartSlab.annualInterestRate, chartSlab.currency, periodTypes); + } + + private DepositAccountInterestRateChartSlabData(Long id, String description, EnumOptionData periodType, Integer fromPeriod, Integer toPeriod, + BigDecimal amountRangeFrom, BigDecimal amountRangeTo, BigDecimal annualInterestRate, CurrencyData currency, Collection periodTypes) { + this.id = id; + this.description = description; + this.periodType = periodType; + this.fromPeriod = fromPeriod; + this.toPeriod = toPeriod; + this.amountRangeFrom = amountRangeFrom; + this.amountRangeTo = amountRangeTo; + this.annualInterestRate = annualInterestRate; + this.currency = currency; + this.periodTypes = periodTypes; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountTransactionDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountTransactionDataValidator.java new file mode 100644 index 000000000..9ea827154 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountTransactionDataValidator.java @@ -0,0 +1,190 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.data; + +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.activatedOnDateParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.bankNumberParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.checkNumberParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.closedOnDateParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.onAccountClosureIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.paymentTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.receiptNumberParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.routingCodeParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.toSavingsAccountIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.transactionAccountNumberParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.transactionAmountParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.transactionDateParamName; + +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.portfolio.savings.DepositAccountOnClosureType; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.DepositsApiConstants; +import org.mifosplatform.portfolio.savings.SavingsApiConstants; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.google.gson.JsonElement; +import com.google.gson.reflect.TypeToken; + +@Component +public class DepositAccountTransactionDataValidator { + + private final FromJsonHelper fromApiJsonHelper; + + @Autowired + public DepositAccountTransactionDataValidator(final FromJsonHelper fromApiJsonHelper) { + this.fromApiJsonHelper = fromApiJsonHelper; + } + + public void validate(final JsonCommand command, DepositAccountType depositAccountType) { + + final String json = command.json(); + + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, + DepositsApiConstants.DEPOSIT_ACCOUNT_TRANSACTION_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(depositAccountType.resourceName()); + + final JsonElement element = command.parsedJson(); + + final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed(transactionDateParamName, element); + baseDataValidator.reset().parameter(transactionDateParamName).value(transactionDate).notNull(); + + final BigDecimal transactionAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(transactionAmountParamName, element); + baseDataValidator.reset().parameter(transactionAmountParamName).value(transactionAmount).notNull().positiveAmount(); + + // Validate all string payment detail fields for max length + final Integer paymentTypeId = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(paymentTypeIdParamName, element); + baseDataValidator.reset().parameter(paymentTypeIdParamName).value(paymentTypeId).ignoreIfNull().integerGreaterThanZero(); + final Set paymentDetailParameters = new HashSet(Arrays.asList(transactionAccountNumberParamName, + checkNumberParamName, routingCodeParamName, receiptNumberParamName, bankNumberParamName)); + for (final String paymentDetailParameterName : paymentDetailParameters) { + final String paymentDetailParameterValue = this.fromApiJsonHelper.extractStringNamed(paymentDetailParameterName, element); + baseDataValidator.reset().parameter(paymentDetailParameterName).value(paymentDetailParameterValue).ignoreIfNull() + .notExceedingLengthOf(50); + } + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public void validateActivation(final JsonCommand command) { + final String json = command.json(); + + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, + SavingsApiConstants.SAVINGS_ACCOUNT_ACTIVATION_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME); + + final JsonElement element = command.parsedJson(); + + final LocalDate activationDate = this.fromApiJsonHelper.extractLocalDateNamed(activatedOnDateParamName, element); + baseDataValidator.reset().parameter(activatedOnDateParamName).value(activationDate).notNull(); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public void validatePreMatureAmountCalculation(final String json, final DepositAccountType depositAccountType) { + + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, + DepositsApiConstants.DEPOSIT_ACCOUNT_PRE_MATURE_CALCULATION_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(depositAccountType + .resourceName()); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final LocalDate closeDate = this.fromApiJsonHelper.extractLocalDateNamed(closedOnDateParamName, element); + baseDataValidator.reset().parameter(closedOnDateParamName).value(closeDate).notNull(); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public void validateClosing(final JsonCommand command, DepositAccountType depositAccountType, final boolean isPreMatureClose) { + final String json = command.json(); + + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, + DepositsApiConstants.DEPOSIT_ACCOUNT_CLOSE_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(depositAccountType + .resourceName()); + + final JsonElement element = command.parsedJson(); + + final LocalDate activationDate = this.fromApiJsonHelper.extractLocalDateNamed(closedOnDateParamName, element); + baseDataValidator.reset().parameter(closedOnDateParamName).value(activationDate).notNull(); + + final Integer onAccountClosureId = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(onAccountClosureIdParamName, element); + baseDataValidator.reset().parameter(onAccountClosureIdParamName).value(onAccountClosureId).notBlank() + .isOneOfTheseValues(DepositAccountOnClosureType.integerValues()); + + if (onAccountClosureId != null) { + final DepositAccountOnClosureType accountOnClosureType = DepositAccountOnClosureType.fromInt(onAccountClosureId); + if (accountOnClosureType.isTransferToSavings()) { + final Long toSavingsAccountId = this.fromApiJsonHelper.extractLongNamed(toSavingsAccountIdParamName, element); + baseDataValidator.reset().parameter(toSavingsAccountIdParamName).value(toSavingsAccountId) + .cantBeBlankWhenParameterProvidedIs(onAccountClosureIdParamName, onAccountClosureId); + } else if (accountOnClosureType.isReinvest() && isPreMatureClose) { + baseDataValidator.reset().parameter(onAccountClosureIdParamName).value(onAccountClosureId) + .failWithCode("reinvest.not.allowed", "Re-Invest is not supported for account pre mature close"); + } + } + + // Validate all string payment detail fields for max length + final Integer paymentTypeId = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(paymentTypeIdParamName, element); + baseDataValidator.reset().parameter(paymentTypeIdParamName).value(paymentTypeId).ignoreIfNull().integerGreaterThanZero(); + final Set paymentDetailParameters = new HashSet(Arrays.asList(transactionAccountNumberParamName, + checkNumberParamName, routingCodeParamName, receiptNumberParamName, bankNumberParamName)); + for (final String paymentDetailParameterName : paymentDetailParameters) { + final String paymentDetailParameterValue = this.fromApiJsonHelper.extractStringNamed(paymentDetailParameterName, element); + baseDataValidator.reset().parameter(paymentDetailParameterName).value(paymentDetailParameterValue).ignoreIfNull() + .notExceedingLengthOf(50); + } + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { + // + throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", + dataValidationErrors); + } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositProductData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositProductData.java new file mode 100644 index 000000000..001dcc841 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositProductData.java @@ -0,0 +1,364 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.data; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.mifosplatform.accounting.common.AccountingRuleType; +import org.mifosplatform.accounting.glaccount.data.GLAccountData; +import org.mifosplatform.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper; +import org.mifosplatform.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper; +import org.mifosplatform.infrastructure.codes.data.CodeValueData; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.organisation.monetary.data.CurrencyData; +import org.mifosplatform.portfolio.charge.data.ChargeData; +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartData; + +/** + * Immutable data object represents a partial data of Deposit Proucts. + */ +public class DepositProductData { + + protected final Long id; + protected final String name; + protected final String shortName; + protected final String description; + protected final CurrencyData currency; + protected final BigDecimal nominalAnnualInterestRate; + protected final EnumOptionData interestCompoundingPeriodType; + protected final EnumOptionData interestPostingPeriodType; + protected final EnumOptionData interestCalculationType; + protected final EnumOptionData interestCalculationDaysInYearType; + // protected final BigDecimal minRequiredOpeningBalance; + protected final Integer lockinPeriodFrequency; + protected final EnumOptionData lockinPeriodFrequencyType; + // protected final boolean withdrawalFeeForTransfers; + + // accounting + protected final EnumOptionData accountingRule; + protected final Map accountingMappings; + protected final Collection paymentChannelToFundSourceMappings; + protected final Collection feeToIncomeAccountMappings; + protected final Collection penaltyToIncomeAccountMappings; + + // charges + protected final Collection charges; + + // interest rate charts + protected final Collection interestRateCharts; + protected final InterestRateChartData activeChart; + + // template + protected final Collection currencyOptions; + protected final Collection interestCompoundingPeriodTypeOptions; + protected final Collection interestPostingPeriodTypeOptions; + protected final Collection interestCalculationTypeOptions; + protected final Collection interestCalculationDaysInYearTypeOptions; + protected final Collection lockinPeriodFrequencyTypeOptions; + protected final Collection withdrawalFeeTypeOptions; + protected final Collection paymentTypeOptions; + protected final Collection accountingRuleOptions; + protected final Map> accountingMappingOptions; + protected final Collection chargeOptions; + protected final Collection penaltyOptions; + protected final InterestRateChartData chartTemplate; + + public static DepositProductData template(final CurrencyData currency, final EnumOptionData interestCompoundingPeriodType, + final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType, + final EnumOptionData interestCalculationDaysInYearType, final EnumOptionData accountingRule, + final Collection currencyOptions, final Collection interestCompoundingPeriodTypeOptions, + final Collection interestPostingPeriodTypeOptions, + final Collection interestCalculationTypeOptions, + final Collection interestCalculationDaysInYearTypeOptions, + final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, + final Collection paymentTypeOptions, final Collection accountingRuleOptions, + final Map> accountingMappingOptions, final Collection chargeOptions, + final Collection penaltyOptions, final InterestRateChartData chartTemplate) { + + final Long id = null; + final String name = null; + final String shortName = null; + final String description = null; + final BigDecimal nominalAnnualInterestRate = null; + final Integer lockinPeriodFrequency = null; + final EnumOptionData lockinPeriodFrequencyType = null; + final Map accountingMappings = null; + final Collection paymentChannelToFundSourceMappings = null; + final Collection charges = null; + final Collection feeToIncomeAccountMappings = null; + final Collection penaltyToIncomeAccountMappings = null; + final Collection interestRateCharts = null; + + return new DepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, + interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, lockinPeriodFrequency, + lockinPeriodFrequencyType, accountingRule, accountingMappings, paymentChannelToFundSourceMappings, currencyOptions, + interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, + accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, + penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate); + } + + public static DepositProductData withCharges(final DepositProductData existingProduct, final Collection charges) { + return new DepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, existingProduct.description, + existingProduct.currency, existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType, + existingProduct.interestPostingPeriodType, existingProduct.interestCalculationType, + existingProduct.interestCalculationDaysInYearType, existingProduct.lockinPeriodFrequency, + existingProduct.lockinPeriodFrequencyType, existingProduct.accountingRule, existingProduct.accountingMappings, + existingProduct.paymentChannelToFundSourceMappings, existingProduct.currencyOptions, + existingProduct.interestCompoundingPeriodTypeOptions, existingProduct.interestPostingPeriodTypeOptions, + existingProduct.interestCalculationTypeOptions, existingProduct.interestCalculationDaysInYearTypeOptions, + existingProduct.lockinPeriodFrequencyTypeOptions, existingProduct.withdrawalFeeTypeOptions, + existingProduct.paymentTypeOptions, existingProduct.accountingRuleOptions, existingProduct.accountingMappingOptions, + charges, existingProduct.chargeOptions, existingProduct.penaltyOptions, existingProduct.feeToIncomeAccountMappings, + existingProduct.penaltyToIncomeAccountMappings, existingProduct.interestRateCharts, existingProduct.chartTemplate); + } + + /** + * Returns a {@link DepositProductData} that contains and exist + * {@link DepositProductData} data with further template data for dropdowns. + */ + public static DepositProductData withTemplate(final DepositProductData existingProduct, final Collection currencyOptions, + final Collection interestCompoundingPeriodTypeOptions, + final Collection interestPostingPeriodTypeOptions, + final Collection interestCalculationTypeOptions, + final Collection interestCalculationDaysInYearTypeOptions, + final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, + final Collection paymentTypeOptions, final Collection accountingRuleOptions, + final Map> accountingMappingOptions, final Collection chargeOptions, + final Collection penaltyOptions, final InterestRateChartData chartTemplate) { + + return new DepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, existingProduct.description, + existingProduct.currency, existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType, + existingProduct.interestPostingPeriodType, existingProduct.interestCalculationType, + existingProduct.interestCalculationDaysInYearType, existingProduct.lockinPeriodFrequency, + existingProduct.lockinPeriodFrequencyType, existingProduct.accountingRule, existingProduct.accountingMappings, + existingProduct.paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions, + interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, + lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, + accountingMappingOptions, existingProduct.charges, chargeOptions, penaltyOptions, + existingProduct.feeToIncomeAccountMappings, existingProduct.penaltyToIncomeAccountMappings, + existingProduct.interestRateCharts, chartTemplate); + } + + public static DepositProductData withAccountingDetails(final DepositProductData existingProduct, + final Map accountingMappings, + final Collection paymentChannelToFundSourceMappings, + final Collection feeToIncomeAccountMappings, + final Collection penaltyToIncomeAccountMappings) { + + final Collection currencyOptions = null; + final Collection interestCompoundingPeriodTypeOptions = null; + final Collection interestPostingPeriodTypeOptions = null; + final Collection interestCalculationTypeOptions = null; + final Collection interestCalculationDaysInYearTypeOptions = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + final Collection paymentTypeOptions = null; + final Collection accountingRuleOptions = null; + final Map> accountingMappingOptions = null; + final Collection chargeOptions = null; + final Collection penaltyOptions = null; + + return new DepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, existingProduct.description, + existingProduct.currency, existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType, + existingProduct.interestPostingPeriodType, existingProduct.interestCalculationType, + existingProduct.interestCalculationDaysInYearType, existingProduct.lockinPeriodFrequency, + existingProduct.lockinPeriodFrequencyType, existingProduct.accountingRule, accountingMappings, + paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions, + interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, + lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, + accountingMappingOptions, existingProduct.charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, + penaltyToIncomeAccountMappings, existingProduct.interestRateCharts, existingProduct.chartTemplate); + } + + public static DepositProductData instance(final Long id, final String name, final String shortName, final String description, + final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, final EnumOptionData interestCompoundingPeriodType, + final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType, + final EnumOptionData interestCalculationDaysInYearType, final Integer lockinPeriodFrequency, + final EnumOptionData lockinPeriodFrequencyType, final EnumOptionData accountingType) { + + final Map accountingMappings = null; + final Collection paymentChannelToFundSourceMappings = null; + + final Collection currencyOptions = null; + final Collection interestCompoundingPeriodTypeOptions = null; + final Collection interestPostingPeriodTypeOptions = null; + final Collection interestCalculationTypeOptions = null; + final Collection interestCalculationDaysInYearTypeOptions = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + final Collection paymentTypeOptions = null; + final Collection accountingRuleOptions = null; + final Map> accountingMappingOptions = null; + final Collection chargeOptions = null; + final Collection penaltyOptions = null; + final Collection charges = null; + final Collection feeToIncomeAccountMappings = null; + final Collection penaltyToIncomeAccountMappings = null; + final Collection interestRateCharts = null; + final InterestRateChartData chartTemplate = null; + + return new DepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, + interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, lockinPeriodFrequency, + lockinPeriodFrequencyType, accountingType, accountingMappings, paymentChannelToFundSourceMappings, currencyOptions, + interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, + accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, + penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate); + } + + public static DepositProductData lookup(final Long id, final String name) { + + final String shortName = null; + final CurrencyData currency = null; + final String description = null; + final BigDecimal nominalAnnualInterestRate = null; + final EnumOptionData interestCompoundingPeriodType = null; + final EnumOptionData interestPostingPeriodType = null; + final EnumOptionData interestCalculationType = null; + final EnumOptionData interestCalculationDaysInYearType = null; + final Integer lockinPeriodFrequency = null; + final EnumOptionData lockinPeriodFrequencyType = null; + + final EnumOptionData accountingType = null; + final Map accountingMappings = null; + final Collection paymentChannelToFundSourceMappings = null; + + final Collection currencyOptions = null; + final Collection interestCompoundingPeriodTypeOptions = null; + final Collection interestPostingPeriodTypeOptions = null; + final Collection interestCalculationTypeOptions = null; + final Collection interestCalculationDaysInYearTypeOptions = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + final Collection paymentTypeOptions = null; + final Collection accountingRuleOptions = null; + final Map> accountingMappingOptions = null; + final Collection charges = null; + final Collection chargeOptions = null; + final Collection penaltyOptions = null; + final Collection feeToIncomeAccountMappings = null; + final Collection penaltyToIncomeAccountMappings = null; + final Collection interestRateCharts = null; + final InterestRateChartData chartTemplate = null; + + return new DepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, + interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, lockinPeriodFrequency, + lockinPeriodFrequencyType, accountingType, accountingMappings, paymentChannelToFundSourceMappings, currencyOptions, + interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, + accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, + penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate); + } + + public static DepositProductData withInterestChart(final DepositProductData existingProduct, + final Collection interestRateCharts) { + return new DepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, existingProduct.description, + existingProduct.currency, existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType, + existingProduct.interestPostingPeriodType, existingProduct.interestCalculationType, + existingProduct.interestCalculationDaysInYearType, existingProduct.lockinPeriodFrequency, + existingProduct.lockinPeriodFrequencyType, existingProduct.accountingRule, existingProduct.accountingMappings, + existingProduct.paymentChannelToFundSourceMappings, existingProduct.currencyOptions, + existingProduct.interestCompoundingPeriodTypeOptions, existingProduct.interestPostingPeriodTypeOptions, + existingProduct.interestCalculationTypeOptions, existingProduct.interestCalculationDaysInYearTypeOptions, + existingProduct.lockinPeriodFrequencyTypeOptions, existingProduct.withdrawalFeeTypeOptions, + existingProduct.paymentTypeOptions, existingProduct.accountingRuleOptions, existingProduct.accountingMappingOptions, + existingProduct.charges, existingProduct.chargeOptions, existingProduct.penaltyOptions, + existingProduct.feeToIncomeAccountMappings, existingProduct.penaltyToIncomeAccountMappings, interestRateCharts, + existingProduct.chartTemplate); + } + + protected DepositProductData(final Long id, final String name, final String shortName, final String description, + final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, final EnumOptionData interestCompoundingPeriodType, + final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType, + final EnumOptionData interestCalculationDaysInYearType, final Integer lockinPeriodFrequency, + final EnumOptionData lockinPeriodFrequencyType, final EnumOptionData accountingType, + final Map accountingMappings, + final Collection paymentChannelToFundSourceMappings, + final Collection currencyOptions, final Collection interestCompoundingPeriodTypeOptions, + final Collection interestPostingPeriodTypeOptions, + final Collection interestCalculationTypeOptions, + final Collection interestCalculationDaysInYearTypeOptions, + final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, + final Collection paymentTypeOptions, final Collection accountingRuleOptions, + final Map> accountingMappingOptions, final Collection charges, + final Collection chargeOptions, final Collection penaltyOptions, + final Collection feeToIncomeAccountMappings, + final Collection penaltyToIncomeAccountMappings, + final Collection interestRateCharts, final InterestRateChartData chartTemplate) { + + this.id = id; + this.name = name; + this.shortName = shortName; + this.description = description; + this.currency = currency; + this.nominalAnnualInterestRate = nominalAnnualInterestRate; + this.interestCompoundingPeriodType = interestCompoundingPeriodType; + this.interestPostingPeriodType = interestPostingPeriodType; + this.interestCalculationType = interestCalculationType; + this.interestCalculationDaysInYearType = interestCalculationDaysInYearType; + this.accountingRule = accountingType; + this.lockinPeriodFrequency = lockinPeriodFrequency; + this.lockinPeriodFrequencyType = lockinPeriodFrequencyType; + + this.currencyOptions = currencyOptions; + this.interestCompoundingPeriodTypeOptions = interestCompoundingPeriodTypeOptions; + this.interestPostingPeriodTypeOptions = interestPostingPeriodTypeOptions; + this.interestCalculationTypeOptions = interestCalculationTypeOptions; + this.interestCalculationDaysInYearTypeOptions = interestCalculationDaysInYearTypeOptions; + this.lockinPeriodFrequencyTypeOptions = lockinPeriodFrequencyTypeOptions; + this.withdrawalFeeTypeOptions = withdrawalFeeTypeOptions; + + this.paymentTypeOptions = paymentTypeOptions; + this.accountingMappingOptions = accountingMappingOptions; + this.accountingRuleOptions = accountingRuleOptions; + if (accountingMappings == null || accountingMappings.isEmpty()) { + this.accountingMappings = null; + } else { + this.accountingMappings = accountingMappings; + } + this.paymentChannelToFundSourceMappings = paymentChannelToFundSourceMappings; + + this.charges = charges;// charges associated with Savings product + this.chargeOptions = chargeOptions;// charges available for adding to + // Savings product + this.penaltyOptions = penaltyOptions;// penalties available for adding + // to Savings product + + this.feeToIncomeAccountMappings = feeToIncomeAccountMappings; + this.penaltyToIncomeAccountMappings = penaltyToIncomeAccountMappings; + this.interestRateCharts = interestRateCharts; + this.activeChart = activeChart(this.interestRateCharts); + this.chartTemplate = chartTemplate; + } + + public static InterestRateChartData activeChart(Collection interestRateCharts) { + InterestRateChartData activeChart = null; + if (interestRateCharts != null) { + for (InterestRateChartData chartData : interestRateCharts) { + if (activeChart == null) { + activeChart = chartData; + } else { + if (!activeChart.isFromDateAfter(chartData.endDate())) { + activeChart = chartData; + } + } + } + } + return activeChart; + } + + public boolean hasAccountingEnabled() { + return this.accountingRule.getId() > AccountingRuleType.NONE.getValue(); + } + + public int accountingRuleTypeId() { + return this.accountingRule.getId().intValue(); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositProductDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositProductDataValidator.java new file mode 100644 index 000000000..c4937ce85 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositProductDataValidator.java @@ -0,0 +1,736 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.data; + +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.idParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_REQUEST_DATA_PARAMETERS; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_REQUEST_DATA_PARAMETERS; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.chartsParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.minDepositTermTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.maxDepositTermTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.inMultiplesOfDepositTermParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.inMultiplesOfDepositTermTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.interestFreeFromPeriodParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.interestFreePeriodApplicableParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.interestFreePeriodFrequencyTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.interestFreeToPeriodParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.maxDepositTermParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.minDepositTermParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.preClosurePenalApplicableParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.preClosurePenalInterestOnTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.preClosurePenalInterestParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositFrequencyParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositFrequencyTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositTypeIdParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.currencyCodeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.descriptionParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeAmountParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeOnMonthDayParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.inMultiplesOfParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nameParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.shortNameParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; + +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.lang.StringUtils; +import org.joda.time.MonthDay; +import org.mifosplatform.accounting.common.AccountingConstants.SAVINGS_PRODUCT_ACCOUNTING_PARAMS; +import org.mifosplatform.accounting.common.AccountingRuleType; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.portfolio.interestratechart.InterestRateChartPeriodType; +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartDataValidator; +import org.mifosplatform.portfolio.savings.PreClosurePenalInterestOnType; +import org.mifosplatform.portfolio.savings.RecurringDepositType; +import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; +import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; +import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; + +@Component +public class DepositProductDataValidator { + + private final FromJsonHelper fromApiJsonHelper; + private final InterestRateChartDataValidator chartDataValidator; + + @Autowired + public DepositProductDataValidator(FromJsonHelper fromApiJsonHelper, InterestRateChartDataValidator chartDataValidator) { + this.fromApiJsonHelper = fromApiJsonHelper; + this.chartDataValidator = chartDataValidator; + } + + public void validateForFixedDepositCreate(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, FIXED_DEPOSIT_PRODUCT_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + + validateDepositDetailForCreate(element, this.fromApiJsonHelper, baseDataValidator); + + validatePreClosureDetailForCreate(element, baseDataValidator); + + validateDepositTermDeatilForCreate(element, baseDataValidator); + + validateChartsData(element, baseDataValidator); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public void validateForFixedDepositUpdate(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, FIXED_DEPOSIT_PRODUCT_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME); + final JsonElement element = this.fromApiJsonHelper.parse(json); + + validateDepositDetailForUpdate(element, fromApiJsonHelper, baseDataValidator); + + validatePreClosureDetailForUpdate(element, baseDataValidator); + + validateDepositTermDetailForUpdate(element, baseDataValidator); + + validateChartsData(element, baseDataValidator); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public void validateForRecurringDepositCreate(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, RECURRING_DEPOSIT_PRODUCT_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + + validateDepositDetailForCreate(element, this.fromApiJsonHelper, baseDataValidator); + + validatePreClosureDetailForCreate(element, baseDataValidator); + + validateDepositTermDeatilForCreate(element, baseDataValidator); + + validateRecurringDetailForCreate(element, baseDataValidator); + + validateChartsData(element, baseDataValidator); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public void validateForRecurringDepositUpdate(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, RECURRING_DEPOSIT_PRODUCT_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME); + final JsonElement element = this.fromApiJsonHelper.parse(json); + + validateDepositDetailForUpdate(element, fromApiJsonHelper, baseDataValidator); + + validatePreClosureDetailForUpdate(element, baseDataValidator); + + validateDepositTermDetailForUpdate(element, baseDataValidator); + + validateRecurringDepositUpdate(element, baseDataValidator); + + validateChartsData(element, baseDataValidator); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + private void validateDepositDetailForCreate(final JsonElement element, final FromJsonHelper fromApiJsonHelper, + final DataValidatorBuilder baseDataValidator) { + final String name = fromApiJsonHelper.extractStringNamed(nameParamName, element); + baseDataValidator.reset().parameter(nameParamName).value(name).notBlank().notExceedingLengthOf(100); + + final String shortName = fromApiJsonHelper.extractStringNamed(shortNameParamName, element); + baseDataValidator.reset().parameter(shortNameParamName).value(shortName).notBlank().notExceedingLengthOf(4); + + final String description = fromApiJsonHelper.extractStringNamed(descriptionParamName, element); + baseDataValidator.reset().parameter(descriptionParamName).value(description).notBlank().notExceedingLengthOf(500); + + final String currencyCode = fromApiJsonHelper.extractStringNamed(currencyCodeParamName, element); + baseDataValidator.reset().parameter(currencyCodeParamName).value(currencyCode).notBlank(); + + final Integer digitsAfterDecimal = fromApiJsonHelper.extractIntegerSansLocaleNamed(digitsAfterDecimalParamName, element); + baseDataValidator.reset().parameter(digitsAfterDecimalParamName).value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6); + + if (fromApiJsonHelper.parameterExists(inMultiplesOfParamName, element)) { + final Integer inMultiplesOf = fromApiJsonHelper.extractIntegerNamed(inMultiplesOfParamName, element, Locale.getDefault()); + baseDataValidator.reset().parameter(inMultiplesOfParamName).value(inMultiplesOf).ignoreIfNull().integerZeroOrGreater(); + } + + if (fromApiJsonHelper.parameterExists(nominalAnnualInterestRateParamName, element)) { + final BigDecimal nominalAnnualInterestRate = fromApiJsonHelper.extractBigDecimalWithLocaleNamed( + nominalAnnualInterestRateParamName, element); + baseDataValidator.reset().parameter(nominalAnnualInterestRateParamName).value(nominalAnnualInterestRate).notNull() + .zeroOrPositiveAmount(); + } + final Integer interestCompoundingPeriodType = fromApiJsonHelper.extractIntegerSansLocaleNamed( + interestCompoundingPeriodTypeParamName, element); + baseDataValidator.reset().parameter(interestCompoundingPeriodTypeParamName).value(interestCompoundingPeriodType).notNull() + .isOneOfTheseValues(SavingsCompoundingInterestPeriodType.integerValues()); + + final Integer interestPostingPeriodType = fromApiJsonHelper.extractIntegerSansLocaleNamed(interestPostingPeriodTypeParamName, + element); + baseDataValidator.reset().parameter(interestPostingPeriodTypeParamName).value(interestPostingPeriodType).notNull() + .isOneOfTheseValues(SavingsPostingInterestPeriodType.integerValues()); + + final Integer interestCalculationType = fromApiJsonHelper.extractIntegerSansLocaleNamed(interestCalculationTypeParamName, element); + baseDataValidator.reset().parameter(interestCalculationTypeParamName).value(interestCalculationType).notNull() + .isOneOfTheseValues(SavingsInterestCalculationType.integerValues()); + + final Integer interestCalculationDaysInYearType = fromApiJsonHelper.extractIntegerSansLocaleNamed( + interestCalculationDaysInYearTypeParamName, element); + baseDataValidator.reset().parameter(interestCalculationDaysInYearTypeParamName).value(interestCalculationDaysInYearType).notNull() + .isOneOfTheseValues(SavingsInterestCalculationDaysInYearType.integerValues()); + + /* + * if + * (fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName + * , element)) { final BigDecimal minOpeningBalance = + * fromApiJsonHelper.extractBigDecimalWithLocaleNamed + * (minRequiredOpeningBalanceParamName, element); + * baseDataValidator.reset + * ().parameter(minRequiredOpeningBalanceParamName + * ).value(minOpeningBalance).zeroOrPositiveAmount(); } + */ + + if (fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) { + + final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, element); + baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).integerZeroOrGreater(); + + if (lockinPeriodFrequency != null) { + final Integer lockinPeriodFrequencyType = fromApiJsonHelper.extractIntegerSansLocaleNamed( + lockinPeriodFrequencyTypeParamName, element); + baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).notNull() + .inMinMaxRange(0, 3); + } + } + + if (fromApiJsonHelper.parameterExists(lockinPeriodFrequencyTypeParamName, element)) { + final Integer lockinPeriodFrequencyType = fromApiJsonHelper.extractIntegerSansLocaleNamed(lockinPeriodFrequencyTypeParamName, + element); + baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).inMinMaxRange(0, 3); + + if (lockinPeriodFrequencyType != null) { + final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, + element); + baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).notNull() + .integerZeroOrGreater(); + } + } + + // accounting related data validation + final Integer accountingRuleType = fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault()); + baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 3); + + if (isCashBasedAccounting(accountingRuleType)) { + + final Long savingsControlAccountId = fromApiJsonHelper.extractLongNamed( + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_CONTROL.getValue(), element); + baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_CONTROL.getValue()) + .value(savingsControlAccountId).notNull().integerGreaterThanZero(); + + final Long savingsReferenceAccountId = fromApiJsonHelper.extractLongNamed( + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_REFERENCE.getValue(), element); + baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_REFERENCE.getValue()) + .value(savingsReferenceAccountId).notNull().integerGreaterThanZero(); + + final Long transfersInSuspenseAccountId = fromApiJsonHelper.extractLongNamed( + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), element); + baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue()) + .value(transfersInSuspenseAccountId).notNull().integerGreaterThanZero(); + + final Long interestOnSavingsAccountId = fromApiJsonHelper.extractLongNamed( + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_SAVINGS.getValue(), element); + baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_SAVINGS.getValue()) + .value(interestOnSavingsAccountId).notNull().integerGreaterThanZero(); + + final Long incomeFromFeeId = fromApiJsonHelper.extractLongNamed(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), + element); + baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue()).value(incomeFromFeeId) + .notNull().integerGreaterThanZero(); + + final Long incomeFromPenaltyId = fromApiJsonHelper.extractLongNamed( + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), element); + baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue()) + .value(incomeFromPenaltyId).notNull().integerGreaterThanZero(); + + validatePaymentChannelFundSourceMappings(fromApiJsonHelper, baseDataValidator, element); + validateChargeToIncomeAccountMappings(fromApiJsonHelper, baseDataValidator, element); + } + } + + public void validatePreClosureDetailForCreate(JsonElement element, DataValidatorBuilder baseDataValidator) { + if (fromApiJsonHelper.parameterExists(interestFreePeriodApplicableParamName, element)) { + + final boolean interestFreePeriodApplicable = fromApiJsonHelper.extractBooleanNamed(interestFreePeriodApplicableParamName, + element); + + if (interestFreePeriodApplicable) { + final Integer interestFreeFromPeriod = fromApiJsonHelper.extractIntegerSansLocaleNamed(interestFreeFromPeriodParamName, + element); + baseDataValidator.reset().parameter(interestFreeFromPeriodParamName).value(interestFreeFromPeriod) + .cantBeBlankWhenParameterProvidedIs(interestFreePeriodApplicableParamName, interestFreePeriodApplicable) + .integerZeroOrGreater(); + + final Integer interestFreeToPeriod = fromApiJsonHelper + .extractIntegerSansLocaleNamed(interestFreeToPeriodParamName, element); + baseDataValidator.reset().parameter(interestFreeToPeriodParamName).value(interestFreeToPeriod) + .cantBeBlankWhenParameterProvidedIs(interestFreePeriodApplicableParamName, interestFreePeriodApplicable) + .integerZeroOrGreater().integerGreaterThanNumber(interestFreeFromPeriod); + + final Integer periodFrequencyType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed( + interestFreePeriodFrequencyTypeIdParamName, element); + baseDataValidator.reset().parameter(interestFreePeriodFrequencyTypeIdParamName).value(periodFrequencyType) + .cantBeBlankWhenParameterProvidedIs(interestFreePeriodApplicableParamName, interestFreePeriodApplicable) + .isOneOfTheseValues(InterestRateChartPeriodType.integerValues()); + } + } + + if (fromApiJsonHelper.parameterExists(preClosurePenalApplicableParamName, element)) { + final boolean preClosurePenalApplicable = fromApiJsonHelper.extractBooleanNamed(preClosurePenalApplicableParamName, element); + + if (preClosurePenalApplicable) { + final BigDecimal penalInterestRate = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(preClosurePenalInterestParamName, + element); + baseDataValidator.reset().parameter(preClosurePenalInterestParamName).value(penalInterestRate) + .cantBeBlankWhenParameterProvidedIs(preClosurePenalApplicableParamName, preClosurePenalApplicable) + .zeroOrPositiveAmount(); + + final Integer preClosurePenalInterestType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed( + preClosurePenalInterestOnTypeIdParamName, element); + baseDataValidator.reset().parameter(preClosurePenalInterestOnTypeIdParamName).value(preClosurePenalInterestType) + .cantBeBlankWhenParameterProvidedIs(preClosurePenalApplicableParamName, preClosurePenalApplicable) + .isOneOfTheseValues(PreClosurePenalInterestOnType.integerValues()); + } + } + } + + public void validateDepositTermDeatilForCreate(JsonElement element, DataValidatorBuilder baseDataValidator) { + + final Integer minTerm = fromApiJsonHelper.extractIntegerSansLocaleNamed(minDepositTermParamName, element); + baseDataValidator.reset().parameter(minDepositTermParamName).value(minTerm).integerGreaterThanZero(); + + if (fromApiJsonHelper.parameterExists(maxDepositTermParamName, element)) { + final Integer maxTerm = fromApiJsonHelper.extractIntegerSansLocaleNamed(maxDepositTermParamName, element); + baseDataValidator.reset().parameter(maxDepositTermParamName).value(maxTerm).integerGreaterThanZero(); + } + + final Integer minDepositTermType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(minDepositTermTypeIdParamName, element); + baseDataValidator.reset().parameter(minDepositTermTypeIdParamName).value(minDepositTermType) + .isOneOfTheseValues(SavingsPeriodFrequencyType.integerValues()); + + if (fromApiJsonHelper.parameterExists(maxDepositTermTypeIdParamName, element)) { + final Integer maxDepositTermType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(maxDepositTermTypeIdParamName, element); + baseDataValidator.reset().parameter(maxDepositTermTypeIdParamName).value(maxDepositTermType) + .isOneOfTheseValues(SavingsPeriodFrequencyType.integerValues()); + } + + if (fromApiJsonHelper.parameterExists(inMultiplesOfDepositTermParamName, element)) { + final Integer inMultiplesOfDepositTerm = fromApiJsonHelper.extractIntegerSansLocaleNamed(inMultiplesOfDepositTermParamName, + element); + baseDataValidator.reset().parameter(inMultiplesOfDepositTermParamName).value(inMultiplesOfDepositTerm).integerGreaterThanZero(); + final Integer inMultiplesOfDepositTermType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed( + inMultiplesOfDepositTermTypeIdParamName, element); + baseDataValidator.reset().parameter(inMultiplesOfDepositTermTypeIdParamName).value(inMultiplesOfDepositTermType) + .cantBeBlankWhenParameterProvidedIs(inMultiplesOfDepositTermParamName, inMultiplesOfDepositTerm) + .isOneOfTheseValues(SavingsPeriodFrequencyType.integerValues()); + } + } + + private void validateChartsData(JsonElement element, DataValidatorBuilder baseDataValidator) { + if (element.isJsonObject()) { + final JsonObject topLevelJsonElement = element.getAsJsonObject(); + if (topLevelJsonElement.has(chartsParamName) && topLevelJsonElement.get(chartsParamName).isJsonArray()) { + final JsonArray array = topLevelJsonElement.get(chartsParamName).getAsJsonArray(); + for (int i = 0; i < array.size(); i++) { + final JsonObject interestRateChartElement = array.get(i).getAsJsonObject(); + final String json = this.fromApiJsonHelper.toJson(interestRateChartElement); + // chart for create + if (!this.fromApiJsonHelper.parameterExists(idParamName, interestRateChartElement)) { + this.chartDataValidator.validateForCreate(json, baseDataValidator); + } else { // chart for update + this.chartDataValidator.validateForUpdate(json, baseDataValidator); + } + } + } + } + } + + public void validateDepositDetailForUpdate(final JsonElement element, final FromJsonHelper fromApiJsonHelper, + final DataValidatorBuilder baseDataValidator) { + if (fromApiJsonHelper.parameterExists(nameParamName, element)) { + final String name = fromApiJsonHelper.extractStringNamed(nameParamName, element); + baseDataValidator.reset().parameter(nameParamName).value(name).notBlank().notExceedingLengthOf(100); + } + + if (fromApiJsonHelper.parameterExists(shortNameParamName, element)) { + final String shortName = fromApiJsonHelper.extractStringNamed(shortNameParamName, element); + baseDataValidator.reset().parameter(shortNameParamName).value(shortName).notBlank().notExceedingLengthOf(4); + } + + if (fromApiJsonHelper.parameterExists(descriptionParamName, element)) { + final String description = fromApiJsonHelper.extractStringNamed(descriptionParamName, element); + baseDataValidator.reset().parameter(descriptionParamName).value(description).notBlank().notExceedingLengthOf(500); + } + + if (fromApiJsonHelper.parameterExists(currencyCodeParamName, element)) { + final String currencyCode = fromApiJsonHelper.extractStringNamed(currencyCodeParamName, element); + baseDataValidator.reset().parameter(currencyCodeParamName).value(currencyCode).notBlank(); + } + + if (fromApiJsonHelper.parameterExists(digitsAfterDecimalParamName, element)) { + final Integer digitsAfterDecimal = fromApiJsonHelper.extractIntegerSansLocaleNamed(digitsAfterDecimalParamName, element); + baseDataValidator.reset().parameter(digitsAfterDecimalParamName).value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6); + } + + if (fromApiJsonHelper.parameterExists(inMultiplesOfParamName, element)) { + final Integer inMultiplesOf = fromApiJsonHelper.extractIntegerNamed(inMultiplesOfParamName, element, Locale.getDefault()); + baseDataValidator.reset().parameter(inMultiplesOfParamName).value(inMultiplesOf).ignoreIfNull().integerZeroOrGreater(); + } + + if (fromApiJsonHelper.parameterExists(nominalAnnualInterestRateParamName, element)) { + final BigDecimal interestRate = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(nominalAnnualInterestRateParamName, element); + baseDataValidator.reset().parameter(nominalAnnualInterestRateParamName).value(interestRate).notNull().zeroOrPositiveAmount(); + } + + if (fromApiJsonHelper.parameterExists(interestCompoundingPeriodTypeParamName, element)) { + final Integer interestCompoundingPeriodType = fromApiJsonHelper.extractIntegerSansLocaleNamed( + interestCompoundingPeriodTypeParamName, element); + baseDataValidator.reset().parameter(interestCompoundingPeriodTypeParamName).value(interestCompoundingPeriodType).notNull() + .isOneOfTheseValues(SavingsCompoundingInterestPeriodType.integerValues()); + } + + if (fromApiJsonHelper.parameterExists(interestCalculationTypeParamName, element)) { + final Integer interestCalculationType = fromApiJsonHelper.extractIntegerSansLocaleNamed(interestCalculationTypeParamName, + element); + baseDataValidator.reset().parameter(interestCalculationTypeParamName).value(interestCalculationType).notNull() + .inMinMaxRange(1, 2); + } + + if (fromApiJsonHelper.parameterExists(interestCalculationDaysInYearTypeParamName, element)) { + final Integer interestCalculationDaysInYearType = fromApiJsonHelper.extractIntegerSansLocaleNamed( + interestCalculationDaysInYearTypeParamName, element); + baseDataValidator.reset().parameter(interestCalculationDaysInYearTypeParamName).value(interestCalculationDaysInYearType) + .notNull().isOneOfTheseValues(360, 365); + } + + if (fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) { + final BigDecimal minOpeningBalance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minRequiredOpeningBalanceParamName, + element); + baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).ignoreIfNull() + .zeroOrPositiveAmount(); + } + + if (fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) { + final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, element); + baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).ignoreIfNull() + .integerZeroOrGreater(); + } + + if (fromApiJsonHelper.parameterExists(lockinPeriodFrequencyTypeParamName, element)) { + final Integer lockinPeriodFrequencyType = fromApiJsonHelper.extractIntegerSansLocaleNamed(lockinPeriodFrequencyTypeParamName, + element); + baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).inMinMaxRange(0, 3); + } + + if (fromApiJsonHelper.parameterExists(withdrawalFeeForTransfersParamName, element)) { + final Boolean isWithdrawalFeeApplicableForTransfers = fromApiJsonHelper.extractBooleanNamed(withdrawalFeeForTransfersParamName, + element); + baseDataValidator.reset().parameter(withdrawalFeeForTransfersParamName).value(isWithdrawalFeeApplicableForTransfers) + .ignoreIfNull().validateForBooleanValue(); + } + + if (fromApiJsonHelper.parameterExists(feeAmountParamName, element)) { + final BigDecimal annualFeeAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(feeAmountParamName, element); + baseDataValidator.reset().parameter(feeAmountParamName).value(annualFeeAmount).ignoreIfNull().zeroOrPositiveAmount(); + } + + if (fromApiJsonHelper.parameterExists(feeOnMonthDayParamName, element)) { + final MonthDay monthDayOfAnnualFee = fromApiJsonHelper.extractMonthDayNamed(feeOnMonthDayParamName, element); + baseDataValidator.reset().parameter(feeOnMonthDayParamName).value(monthDayOfAnnualFee).ignoreIfNull(); + } + + final Long savingsControlAccountId = fromApiJsonHelper.extractLongNamed( + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_CONTROL.getValue(), element); + baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_CONTROL.getValue()).value(savingsControlAccountId) + .ignoreIfNull().integerGreaterThanZero(); + + final Long savingsReferenceAccountId = fromApiJsonHelper.extractLongNamed( + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_REFERENCE.getValue(), element); + baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_REFERENCE.getValue()) + .value(savingsReferenceAccountId).ignoreIfNull().integerGreaterThanZero(); + + final Long transfersInSuspenseAccountId = fromApiJsonHelper.extractLongNamed( + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), element); + baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue()) + .value(transfersInSuspenseAccountId).ignoreIfNull().integerGreaterThanZero(); + + final Long interestOnSavingsAccountId = fromApiJsonHelper.extractLongNamed( + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_SAVINGS.getValue(), element); + baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_SAVINGS.getValue()) + .value(interestOnSavingsAccountId).ignoreIfNull().integerGreaterThanZero(); + + final Long incomeFromFeeId = fromApiJsonHelper.extractLongNamed(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), + element); + baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue()).value(incomeFromFeeId) + .ignoreIfNull().integerGreaterThanZero(); + + final Long incomeFromPenaltyId = fromApiJsonHelper.extractLongNamed( + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), element); + baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue()).value(incomeFromPenaltyId) + .ignoreIfNull().integerGreaterThanZero(); + + validatePaymentChannelFundSourceMappings(fromApiJsonHelper, baseDataValidator, element); + validateChargeToIncomeAccountMappings(fromApiJsonHelper, baseDataValidator, element); + } + + public void validatePreClosureDetailForUpdate(JsonElement element, DataValidatorBuilder baseDataValidator) { + + if (fromApiJsonHelper.parameterExists(interestFreePeriodApplicableParamName, element)) { + final Boolean interestFreePeriodApplicable = fromApiJsonHelper.extractBooleanNamed(interestFreePeriodApplicableParamName, + element); + baseDataValidator.reset().parameter(interestFreePeriodApplicableParamName).value(interestFreePeriodApplicable).notNull(); + } + + if (fromApiJsonHelper.parameterExists(interestFreeFromPeriodParamName, element)) { + + final Integer interestFreeFromPeriod = fromApiJsonHelper + .extractIntegerSansLocaleNamed(interestFreeFromPeriodParamName, element); + baseDataValidator.reset().parameter(interestFreeFromPeriodParamName).value(interestFreeFromPeriod).notNull() + .integerZeroOrGreater(); + } + + if (fromApiJsonHelper.parameterExists(interestFreeToPeriodParamName, element)) { + final Integer interestFreeToPeriod = fromApiJsonHelper.extractIntegerSansLocaleNamed(interestFreeToPeriodParamName, element); + baseDataValidator.reset().parameter(interestFreeToPeriodParamName).value(interestFreeToPeriod).notNull().integerZeroOrGreater(); + } + + if (fromApiJsonHelper.parameterExists(interestFreePeriodFrequencyTypeIdParamName, element)) { + final Integer periodFrequencyType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed( + interestFreePeriodFrequencyTypeIdParamName, element); + baseDataValidator.reset().parameter(interestFreePeriodFrequencyTypeIdParamName).value(periodFrequencyType).notNull() + .isOneOfTheseValues(InterestRateChartPeriodType.integerValues()); + } + + if (fromApiJsonHelper.parameterExists(preClosurePenalApplicableParamName, element)) { + final Boolean preClosurePenalApplicable = fromApiJsonHelper.extractBooleanNamed(preClosurePenalApplicableParamName, element); + baseDataValidator.reset().parameter(preClosurePenalApplicableParamName).value(preClosurePenalApplicable).notNull(); + } + + if (fromApiJsonHelper.parameterExists(preClosurePenalInterestParamName, element)) { + final BigDecimal penalInterestRate = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(preClosurePenalInterestParamName, + element); + baseDataValidator.reset().parameter(preClosurePenalInterestParamName).value(penalInterestRate).notNull().zeroOrPositiveAmount(); + } + + if (fromApiJsonHelper.parameterExists(preClosurePenalInterestOnTypeIdParamName, element)) { + final Integer preClosurePenalInterestType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed( + preClosurePenalInterestOnTypeIdParamName, element); + baseDataValidator.reset().parameter(preClosurePenalInterestOnTypeIdParamName).value(preClosurePenalInterestType).notNull() + .isOneOfTheseValues(PreClosurePenalInterestOnType.integerValues()); + } + } + + public void validateDepositTermDetailForUpdate(JsonElement element, DataValidatorBuilder baseDataValidator) { + + if (fromApiJsonHelper.parameterExists(minDepositTermParamName, element)) { + final Integer minTerm = fromApiJsonHelper.extractIntegerSansLocaleNamed(minDepositTermParamName, element); + baseDataValidator.reset().parameter(minDepositTermParamName).value(minTerm).integerGreaterThanZero(); + } + + if (fromApiJsonHelper.parameterExists(maxDepositTermParamName, element)) { + final Integer maxTerm = fromApiJsonHelper.extractIntegerSansLocaleNamed(maxDepositTermParamName, element); + baseDataValidator.reset().parameter(maxDepositTermParamName).value(maxTerm).integerGreaterThanZero(); + } + + if (fromApiJsonHelper.parameterExists(minDepositTermTypeIdParamName, element)) { + final Integer minDepositTermType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(minDepositTermTypeIdParamName, element); + baseDataValidator.reset().parameter(minDepositTermTypeIdParamName).value(minDepositTermType) + .isOneOfTheseValues(SavingsPeriodFrequencyType.integerValues()); + } + + if (fromApiJsonHelper.parameterExists(maxDepositTermTypeIdParamName, element)) { + final Integer maxDepositTermType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(maxDepositTermTypeIdParamName, element); + baseDataValidator.reset().parameter(maxDepositTermTypeIdParamName).value(maxDepositTermType) + .isOneOfTheseValues(SavingsPeriodFrequencyType.integerValues()); + } + + if (fromApiJsonHelper.parameterExists(inMultiplesOfDepositTermParamName, element)) { + final Integer inMultiplesOfDepositTerm = fromApiJsonHelper.extractIntegerSansLocaleNamed(inMultiplesOfDepositTermParamName, + element); + baseDataValidator.reset().parameter(inMultiplesOfDepositTermParamName).value(inMultiplesOfDepositTerm).integerGreaterThanZero(); + } + + if (fromApiJsonHelper.parameterExists(inMultiplesOfDepositTermTypeIdParamName, element)) { + final Integer inMultiplesOfDepositTermType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed( + inMultiplesOfDepositTermTypeIdParamName, element); + baseDataValidator.reset().parameter(inMultiplesOfDepositTermTypeIdParamName).value(inMultiplesOfDepositTermType) + .isOneOfTheseValues(SavingsPeriodFrequencyType.integerValues()); + } + } + + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + private boolean isCashBasedAccounting(final Integer accountingRuleType) { + return AccountingRuleType.CASH_BASED.getValue().equals(accountingRuleType); + } + + /** + * Validation for advanced accounting options + */ + private void validatePaymentChannelFundSourceMappings(final FromJsonHelper fromApiJsonHelper, + final DataValidatorBuilder baseDataValidator, final JsonElement element) { + if (fromApiJsonHelper.parameterExists(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(), element)) { + final JsonArray paymentChannelMappingArray = fromApiJsonHelper.extractJsonArrayNamed( + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(), element); + if (paymentChannelMappingArray != null && paymentChannelMappingArray.size() > 0) { + int i = 0; + do { + final JsonObject jsonObject = paymentChannelMappingArray.get(i).getAsJsonObject(); + final Long paymentTypeId = jsonObject.get(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_TYPE.getValue()).getAsLong(); + final Long paymentSpecificFundAccountId = jsonObject.get(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue()) + .getAsLong(); + baseDataValidator + .reset() + .parameter( + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue() + "[" + i + "]." + + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_TYPE.toString()).value(paymentTypeId).notNull() + .integerGreaterThanZero(); + baseDataValidator + .reset() + .parameter( + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue() + "[" + i + "]." + + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue()).value(paymentSpecificFundAccountId) + .notNull().integerGreaterThanZero(); + i++; + } while (i < paymentChannelMappingArray.size()); + } + } + } + + private void validateChargeToIncomeAccountMappings(final FromJsonHelper fromApiJsonHelper, + final DataValidatorBuilder baseDataValidator, final JsonElement element) { + // validate for both fee and penalty charges + validateChargeToIncomeAccountMappings(fromApiJsonHelper, baseDataValidator, element, true); + validateChargeToIncomeAccountMappings(fromApiJsonHelper, baseDataValidator, element, true); + } + + private void validateChargeToIncomeAccountMappings(final FromJsonHelper fromApiJsonHelper, + final DataValidatorBuilder baseDataValidator, final JsonElement element, final boolean isPenalty) { + String parameterName; + if (isPenalty) { + parameterName = SAVINGS_PRODUCT_ACCOUNTING_PARAMS.PENALTY_INCOME_ACCOUNT_MAPPING.getValue(); + } else { + parameterName = SAVINGS_PRODUCT_ACCOUNTING_PARAMS.FEE_INCOME_ACCOUNT_MAPPING.getValue(); + } + + if (fromApiJsonHelper.parameterExists(parameterName, element)) { + final JsonArray chargeToIncomeAccountMappingArray = fromApiJsonHelper.extractJsonArrayNamed(parameterName, element); + if (chargeToIncomeAccountMappingArray != null && chargeToIncomeAccountMappingArray.size() > 0) { + int i = 0; + do { + final JsonObject jsonObject = chargeToIncomeAccountMappingArray.get(i).getAsJsonObject(); + final Long chargeId = fromApiJsonHelper.extractLongNamed(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.CHARGE_ID.getValue(), + jsonObject); + final Long incomeAccountId = fromApiJsonHelper.extractLongNamed( + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_ACCOUNT_ID.getValue(), jsonObject); + baseDataValidator.reset() + .parameter(parameterName + "[" + i + "]." + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.CHARGE_ID.getValue()) + .value(chargeId).notNull().integerGreaterThanZero(); + baseDataValidator.reset() + .parameter(parameterName + "[" + i + "]." + SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_ACCOUNT_ID.getValue()) + .value(incomeAccountId).notNull().integerGreaterThanZero(); + i++; + } while (i < chargeToIncomeAccountMappingArray.size()); + } + } + } + + public void validateRecurringDetailForCreate(JsonElement element, DataValidatorBuilder baseDataValidator) { + + final Integer rdTypeId = fromApiJsonHelper.extractIntegerSansLocaleNamed(recurringDepositTypeIdParamName, element); + baseDataValidator.reset().parameter(recurringDepositTypeIdParamName).value(rdTypeId).notNull() + .isOneOfTheseValues(RecurringDepositType.integerValues()); + + final Integer rdFrequency = fromApiJsonHelper.extractIntegerSansLocaleNamed(recurringDepositFrequencyParamName, element); + baseDataValidator.reset().parameter(recurringDepositFrequencyParamName).value(rdFrequency).notNull().integerGreaterThanZero(); + + final Integer rdFrequencyTypeId = fromApiJsonHelper + .extractIntegerSansLocaleNamed(recurringDepositFrequencyTypeIdParamName, element); + baseDataValidator.reset().parameter(recurringDepositFrequencyTypeIdParamName).value(rdFrequencyTypeId).notNull() + .isOneOfTheseValues(SavingsPeriodFrequencyType.integerValues()); + } + + public void validateRecurringDepositUpdate(JsonElement element, DataValidatorBuilder baseDataValidator) { + + if (fromApiJsonHelper.parameterExists(recurringDepositTypeIdParamName, element)) { + final Integer rdTypeId = fromApiJsonHelper.extractIntegerSansLocaleNamed(recurringDepositTypeIdParamName, element); + baseDataValidator.reset().parameter(recurringDepositTypeIdParamName).value(rdTypeId).notNull() + .isOneOfTheseValues(RecurringDepositType.integerValues()); + } + + if (fromApiJsonHelper.parameterExists(recurringDepositFrequencyParamName, element)) { + final Integer rdFrequency = fromApiJsonHelper.extractIntegerSansLocaleNamed(recurringDepositFrequencyParamName, element); + baseDataValidator.reset().parameter(recurringDepositFrequencyParamName).value(rdFrequency).notNull().integerGreaterThanZero(); + } + + if (fromApiJsonHelper.parameterExists(recurringDepositFrequencyTypeIdParamName, element)) { + final Integer rdFrequencyTypeId = fromApiJsonHelper.extractIntegerSansLocaleNamed(recurringDepositFrequencyTypeIdParamName, + element); + baseDataValidator.reset().parameter(recurringDepositFrequencyTypeIdParamName).value(rdFrequencyTypeId).notNull() + .isOneOfTheseValues(SavingsPeriodFrequencyType.integerValues()); + } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/FixedDepositAccountData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/FixedDepositAccountData.java new file mode 100644 index 000000000..75ab2e774 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/FixedDepositAccountData.java @@ -0,0 +1,486 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.data; + +import java.math.BigDecimal; +import java.util.Collection; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.codes.data.CodeValueData; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.organisation.monetary.data.CurrencyData; +import org.mifosplatform.organisation.staff.data.StaffData; +import org.mifosplatform.portfolio.charge.data.ChargeData; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.service.SavingsEnumerations; + +/** + * Immutable data object representing a Fixed Deposit account. + */ +public class FixedDepositAccountData extends DepositAccountData { + + // additional fields + private boolean interestFreePeriodApplicable; + private Integer interestFreeFromPeriod; + private Integer interestFreeToPeriod; + private EnumOptionData interestFreePeriodFrequencyType; + private boolean preClosurePenalApplicable; + private BigDecimal preClosurePenalInterest; + private EnumOptionData preClosurePenalInterestOnType; + private Integer minDepositTerm; + private Integer maxDepositTerm; + private EnumOptionData minDepositTermType; + private EnumOptionData maxDepositTermType; + private Integer inMultiplesOfDepositTerm; + private EnumOptionData inMultiplesOfDepositTermType; + private BigDecimal depositAmount; + private BigDecimal maturityAmount; + private LocalDate maturityDate; + private Integer depositPeriod; + private EnumOptionData depositPeriodFrequency; + + // used for account close + private EnumOptionData onAccountClosure; + + private Collection interestFreePeriodTypeOptions; + private Collection preClosurePenalInterestOnTypeOptions; + private Collection depositTermTypeOptions; + private Collection inMultiplesOfDepositTermTypeOptions; + private Collection depositPeriodFrequencyOptions; + private Collection savingsAccounts; + + // for account close + private Collection onAccountClosureOptions; + private Collection paymentTypeOptions; + + public static FixedDepositAccountData instance(final DepositAccountData depositAccountData, final boolean interestFreePeriodApplicable, + final Integer interestFreeFromPeriod, final Integer interestFreeToPeriod, final EnumOptionData interestFreePeriodFrequencyType, + final boolean preClosurePenalApplicable, final BigDecimal preClosurePenalInterest, + final EnumOptionData preClosurePenalInterestOnType, final Integer minDepositTerm, final Integer maxDepositTerm, + final EnumOptionData minDepositTermType, final EnumOptionData maxDepositTermType, final Integer inMultiplesOfDepositTerm, + final EnumOptionData inMultiplesOfDepositTermType, final BigDecimal depositAmount, final BigDecimal maturityAmount, + final LocalDate maturityDate, final Integer depositPeriod, final EnumOptionData depositPeriodFrequency, + final EnumOptionData onAccountClosure) { + + final Collection interestFreePeriodTypeOptions = null; + final Collection preClosurePenalInterestOnTypeOptions = null; + final Collection depositTermTypeOptions = null; + final Collection inMultiplesOfDepositTermTypeOptions = null; + final Collection depositPeriodFrequencyOptions = null; + final EnumOptionData depositType = SavingsEnumerations.depositType(DepositAccountType.FIXED_DEPOSIT.getValue()); + final Collection onAccountClosureOptions = null; + final Collection paymentTypeOptions = null; + final Collection savingsAccountDatas = null; + + return new FixedDepositAccountData(depositAccountData.id, depositAccountData.accountNo, depositAccountData.externalId, + depositAccountData.groupId, depositAccountData.groupName, depositAccountData.clientId, depositAccountData.clientName, + depositAccountData.savingsProductId, depositAccountData.savingsProductName, depositAccountData.fieldOfficerId, + depositAccountData.fieldOfficerName, depositAccountData.status, depositAccountData.timeline, depositAccountData.currency, + depositAccountData.nominalAnnualInterestRate, depositAccountData.interestCompoundingPeriodType, + depositAccountData.interestPostingPeriodType, depositAccountData.interestCalculationType, + depositAccountData.interestCalculationDaysInYearType, depositAccountData.minRequiredOpeningBalance, + depositAccountData.lockinPeriodFrequency, depositAccountData.lockinPeriodFrequencyType, + depositAccountData.withdrawalFeeForTransfers, depositAccountData.summary, depositAccountData.transactions, + depositAccountData.productOptions, depositAccountData.fieldOfficerOptions, + depositAccountData.interestCompoundingPeriodTypeOptions, depositAccountData.interestPostingPeriodTypeOptions, + depositAccountData.interestCalculationTypeOptions, depositAccountData.interestCalculationDaysInYearTypeOptions, + depositAccountData.lockinPeriodFrequencyTypeOptions, depositAccountData.withdrawalFeeTypeOptions, + depositAccountData.charges, depositAccountData.chargeOptions, depositAccountData.accountChart, + depositAccountData.chartTemplate, interestFreePeriodApplicable, interestFreeFromPeriod, interestFreeToPeriod, + interestFreePeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest, preClosurePenalInterestOnType, + interestFreePeriodTypeOptions, preClosurePenalInterestOnTypeOptions, minDepositTerm, maxDepositTerm, minDepositTermType, + maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, + depositPeriod, depositPeriodFrequency, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions, + depositPeriodFrequencyOptions, depositType, onAccountClosure, onAccountClosureOptions, paymentTypeOptions, + savingsAccountDatas); + } + + public static FixedDepositAccountData withInterestChart(final FixedDepositAccountData account, + final DepositAccountInterestRateChartData accountChart) { + return new FixedDepositAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName, + account.clientId, account.clientName, account.savingsProductId, account.savingsProductName, account.fieldOfficerId, + account.fieldOfficerName, account.status, account.timeline, account.currency, account.nominalAnnualInterestRate, + account.interestCompoundingPeriodType, account.interestPostingPeriodType, account.interestCalculationType, + account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency, + account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers, account.summary, account.transactions, + account.productOptions, account.fieldOfficerOptions, account.interestCompoundingPeriodTypeOptions, + account.interestPostingPeriodTypeOptions, account.interestCalculationTypeOptions, + account.interestCalculationDaysInYearTypeOptions, account.lockinPeriodFrequencyTypeOptions, + account.withdrawalFeeTypeOptions, account.charges, account.chargeOptions, accountChart, account.chartTemplate, + account.interestFreePeriodApplicable, account.interestFreeFromPeriod, account.interestFreeToPeriod, + account.interestFreePeriodFrequencyType, account.preClosurePenalApplicable, account.preClosurePenalInterest, + account.preClosurePenalInterestOnType, account.interestFreePeriodTypeOptions, account.preClosurePenalInterestOnTypeOptions, + account.minDepositTerm, account.maxDepositTerm, account.minDepositTermType, account.maxDepositTermType, + account.inMultiplesOfDepositTerm, account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, + account.maturityDate, account.depositPeriod, account.depositPeriodFrequency, account.depositTermTypeOptions, + account.inMultiplesOfDepositTermTypeOptions, account.depositTermTypeOptions, account.depositType, account.onAccountClosure, + account.onAccountClosureOptions, account.paymentTypeOptions, account.savingsAccounts); + } + + public static FixedDepositAccountData withTemplateOptions(final FixedDepositAccountData account, + final FixedDepositAccountData template, final Collection transactions, + final Collection charges) { + + if (template == null) { + final Collection productOptions = null; + final Collection fieldOfficerOptions = null; + final Collection interestCompoundingPeriodTypeOptions = null; + final Collection interestPostingPeriodTypeOptions = null; + final Collection interestCalculationTypeOptions = null; + final Collection interestCalculationDaysInYearTypeOptions = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + final Collection chargeOptions = null; + + final Collection interestFreePeriodTypeOptions = null; + final Collection preClosurePenalInterestOnTypeOptions = null; + final Collection depositTermTypeOptions = null; + final Collection inMultiplesOfDepositTermTypeOptions = null; + final Collection depositPeriodFrequencyOptions = null; + + return withTemplateOptions(account, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions, + interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, + lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions, charges, chargeOptions, + interestFreePeriodTypeOptions, preClosurePenalInterestOnTypeOptions, depositTermTypeOptions, + inMultiplesOfDepositTermTypeOptions, depositPeriodFrequencyOptions); + } + + return new FixedDepositAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName, + account.clientId, account.clientName, account.savingsProductId, account.savingsProductName, account.fieldOfficerId, + account.fieldOfficerName, account.status, account.timeline, account.currency, account.nominalAnnualInterestRate, + account.interestCompoundingPeriodType, account.interestPostingPeriodType, account.interestCalculationType, + account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency, + account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers, account.summary, transactions, + template.productOptions, template.fieldOfficerOptions, template.interestCompoundingPeriodTypeOptions, + template.interestPostingPeriodTypeOptions, template.interestCalculationTypeOptions, + template.interestCalculationDaysInYearTypeOptions, template.lockinPeriodFrequencyTypeOptions, + template.withdrawalFeeTypeOptions, charges, template.chargeOptions, account.accountChart, account.chartTemplate, + account.interestFreePeriodApplicable, account.interestFreeFromPeriod, account.interestFreeToPeriod, + account.interestFreePeriodFrequencyType, account.preClosurePenalApplicable, account.preClosurePenalInterest, + account.preClosurePenalInterestOnType, template.interestFreePeriodTypeOptions, + template.preClosurePenalInterestOnTypeOptions, account.minDepositTerm, account.maxDepositTerm, account.minDepositTermType, + account.maxDepositTermType, account.inMultiplesOfDepositTerm, account.inMultiplesOfDepositTermType, account.depositAmount, + account.maturityAmount, account.maturityDate, account.depositPeriod, account.depositPeriodFrequency, + template.depositTermTypeOptions, template.inMultiplesOfDepositTermTypeOptions, template.depositPeriodFrequencyOptions, + account.depositType, account.onAccountClosure, account.onAccountClosureOptions, account.paymentTypeOptions, + account.savingsAccounts); + } + + public static FixedDepositAccountData withTemplateOptions(final FixedDepositAccountData account, + final Collection productOptions, final Collection fieldOfficerOptions, + final Collection interestCompoundingPeriodTypeOptions, + final Collection interestPostingPeriodTypeOptions, + final Collection interestCalculationTypeOptions, + final Collection interestCalculationDaysInYearTypeOptions, + final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, + final Collection transactions, final Collection charges, + final Collection chargeOptions, final Collection interestFreePeriodTypeOptions, + final Collection preClosurePenalInterestOnTypeOptions, final Collection depositTermTypeOptions, + final Collection inMultiplesOfDepositTermTypeOptions, + final Collection depositPeriodFrequencyOptions) { + + return new FixedDepositAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName, + account.clientId, account.clientName, account.savingsProductId, account.savingsProductName, account.fieldOfficerId, + account.fieldOfficerName, account.status, account.timeline, account.currency, account.nominalAnnualInterestRate, + account.interestCompoundingPeriodType, account.interestPostingPeriodType, account.interestCalculationType, + account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency, + account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers, account.summary, transactions, productOptions, + fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, + interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, + withdrawalFeeTypeOptions, charges, chargeOptions, account.accountChart, account.chartTemplate, + account.interestFreePeriodApplicable, account.interestFreeFromPeriod, account.interestFreeToPeriod, + account.interestFreePeriodFrequencyType, account.preClosurePenalApplicable, account.preClosurePenalInterest, + account.preClosurePenalInterestOnType, interestFreePeriodTypeOptions, preClosurePenalInterestOnTypeOptions, + account.minDepositTerm, account.maxDepositTerm, account.minDepositTermType, account.maxDepositTermType, + account.inMultiplesOfDepositTerm, account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, + account.maturityDate, account.depositPeriod, account.depositPeriodFrequency, depositTermTypeOptions, + inMultiplesOfDepositTermTypeOptions, depositPeriodFrequencyOptions, account.depositType, account.onAccountClosure, + account.onAccountClosureOptions, account.paymentTypeOptions, account.savingsAccounts); + } + + public static FixedDepositAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId, + final String groupName) { + + final Long id = null; + final String accountNo = null; + final String externalId = null; + final Long productId = null; + final String productName = null; + final Long fieldOfficerId = null; + final String fieldOfficerName = null; + final SavingsAccountStatusEnumData status = null; + final SavingsAccountApplicationTimelineData timeline = null; + final CurrencyData currency = null; + final BigDecimal nominalAnnualInterestRate = null; + final EnumOptionData interestPeriodType = null; + final EnumOptionData interestPostingPeriodType = null; + final EnumOptionData interestCalculationType = null; + final EnumOptionData interestCalculationDaysInYearType = null; + final BigDecimal minRequiredOpeningBalance = null; + final Integer lockinPeriodFrequency = null; + final EnumOptionData lockinPeriodFrequencyType = null; + final boolean withdrawalFeeForTransfers = false; + final SavingsAccountSummaryData summary = null; + final Collection transactions = null; + + final Collection productOptions = null; + final Collection fieldOfficerOptions = null; + final Collection interestCompoundingPeriodTypeOptions = null; + final Collection interestPostingPeriodTypeOptions = null; + final Collection interestCalculationTypeOptions = null; + final Collection interestCalculationDaysInYearTypeOptions = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + + final Collection charges = null; + final Collection chargeOptions = null; + + final DepositAccountInterestRateChartData accountChart = null; + final DepositAccountInterestRateChartData chartTemplate = null; + final boolean interestFreePeriodApplicable = false; + final Integer interestFreeFromPeriod = null; + final Integer interestFreeToPeriod = null; + final EnumOptionData interestFreePeriodFrequencyType = null; + final boolean preClosurePenalApplicable = false; + final BigDecimal preClosurePenalInterest = null; + final EnumOptionData preClosurePenalInterestOnType = null; + final Integer minDepositTerm = null; + final Integer maxDepositTerm = null; + final EnumOptionData minDepositTermType = null; + final EnumOptionData maxDepositTermType = null; + final Integer inMultiplesOfDepositTerm = null; + final EnumOptionData inMultiplesOfDepositTermType = null; + final BigDecimal depositAmount = null; + final BigDecimal maturityAmount = null; + final LocalDate maturityDate = null; + final Integer depositPeriod = null; + final EnumOptionData depositPeriodFrequency = null; + final EnumOptionData onAccountClosure = null; + + final Collection interestFreePeriodTypeOptions = null; + final Collection preClosurePenalInterestOnTypeOptions = null; + final Collection depositTermTypeOptions = null; + final Collection inMultiplesOfDepositTermTypeOptions = null; + final Collection depositPeriodFrequencyOptions = null; + final EnumOptionData depositType = SavingsEnumerations.depositType(DepositAccountType.FIXED_DEPOSIT.getValue()); + final Collection onAccountClosureOptions = null; + final Collection paymentTypeOptions = null; + final Collection savingsAccountDatas = null; + + return new FixedDepositAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, + fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType, + interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions, + fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, + interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, + withdrawalFeeTypeOptions, charges, chargeOptions, accountChart, chartTemplate, interestFreePeriodApplicable, + interestFreeFromPeriod, interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, + preClosurePenalInterest, preClosurePenalInterestOnType, interestFreePeriodTypeOptions, + preClosurePenalInterestOnTypeOptions, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, + inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod, + depositPeriodFrequency, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions, depositPeriodFrequencyOptions, + depositType, onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas); + } + + public static FixedDepositAccountData preClosureDetails(final Long accountId, BigDecimal maturityAmount, + final Collection onAccountClosureOptions, final Collection paymentTypeOptions, + final Collection savingsAccountDatas) { + + final Long groupId = null; + final String groupName = null; + final Long clientId = null; + final String clientName = null; + final String accountNo = null; + final String externalId = null; + final Long productId = null; + final String productName = null; + final Long fieldOfficerId = null; + final String fieldOfficerName = null; + final SavingsAccountStatusEnumData status = null; + final SavingsAccountApplicationTimelineData timeline = null; + final CurrencyData currency = null; + final BigDecimal nominalAnnualInterestRate = null; + final EnumOptionData interestPeriodType = null; + final EnumOptionData interestPostingPeriodType = null; + final EnumOptionData interestCalculationType = null; + final EnumOptionData interestCalculationDaysInYearType = null; + final BigDecimal minRequiredOpeningBalance = null; + final Integer lockinPeriodFrequency = null; + final EnumOptionData lockinPeriodFrequencyType = null; + final boolean withdrawalFeeForTransfers = false; + final SavingsAccountSummaryData summary = null; + final Collection transactions = null; + + final Collection productOptions = null; + final Collection fieldOfficerOptions = null; + final Collection interestCompoundingPeriodTypeOptions = null; + final Collection interestPostingPeriodTypeOptions = null; + final Collection interestCalculationTypeOptions = null; + final Collection interestCalculationDaysInYearTypeOptions = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + + final Collection charges = null; + final Collection chargeOptions = null; + + final DepositAccountInterestRateChartData accountChart = null; + final DepositAccountInterestRateChartData chartTemplate = null; + final boolean interestFreePeriodApplicable = false; + final Integer interestFreeFromPeriod = null; + final Integer interestFreeToPeriod = null; + final EnumOptionData interestFreePeriodFrequencyType = null; + final boolean preClosurePenalApplicable = false; + final BigDecimal preClosurePenalInterest = null; + final EnumOptionData preClosurePenalInterestOnType = null; + final Integer minDepositTerm = null; + final Integer maxDepositTerm = null; + final EnumOptionData minDepositTermType = null; + final EnumOptionData maxDepositTermType = null; + final Integer inMultiplesOfDepositTerm = null; + final EnumOptionData inMultiplesOfDepositTermType = null; + final BigDecimal depositAmount = null; + final LocalDate maturityDate = null; + final Integer depositPeriod = null; + final EnumOptionData depositPeriodFrequency = null; + final EnumOptionData onAccountClosure = null; + + final Collection interestFreePeriodTypeOptions = null; + final Collection preClosurePenalInterestOnTypeOptions = null; + final Collection depositTermTypeOptions = null; + final Collection inMultiplesOfDepositTermTypeOptions = null; + final Collection depositPeriodFrequencyOptions = null; + final EnumOptionData depositType = SavingsEnumerations.depositType(DepositAccountType.FIXED_DEPOSIT.getValue()); + + return new FixedDepositAccountData(accountId, accountNo, externalId, groupId, groupName, clientId, clientName, productId, + productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType, + interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions, + fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, + interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, + withdrawalFeeTypeOptions, charges, chargeOptions, accountChart, chartTemplate, interestFreePeriodApplicable, + interestFreeFromPeriod, interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, + preClosurePenalInterest, preClosurePenalInterestOnType, interestFreePeriodTypeOptions, + preClosurePenalInterestOnTypeOptions, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, + inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod, + depositPeriodFrequency, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions, depositPeriodFrequencyOptions, + depositType, onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas); + } + + public static FixedDepositAccountData withClosureTemplateDetails(final FixedDepositAccountData account, + final Collection onAccountClosureOptions, final Collection paymentTypeOptions, + final Collection savingsAccountDatas) { + + return new FixedDepositAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName, + account.clientId, account.clientName, account.savingsProductId, account.savingsProductName, account.fieldOfficerId, + account.fieldOfficerName, account.status, account.timeline, account.currency, account.nominalAnnualInterestRate, + account.interestCompoundingPeriodType, account.interestPostingPeriodType, account.interestCalculationType, + account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency, + account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers, account.summary, account.transactions, + account.productOptions, account.fieldOfficerOptions, account.interestCompoundingPeriodTypeOptions, + account.interestPostingPeriodTypeOptions, account.interestCalculationTypeOptions, + account.interestCalculationDaysInYearTypeOptions, account.lockinPeriodFrequencyTypeOptions, + account.withdrawalFeeTypeOptions, account.charges, account.chargeOptions, account.accountChart, account.chartTemplate, + account.interestFreePeriodApplicable, account.interestFreeFromPeriod, account.interestFreeToPeriod, + account.interestFreePeriodFrequencyType, account.preClosurePenalApplicable, account.preClosurePenalInterest, + account.preClosurePenalInterestOnType, account.interestFreePeriodTypeOptions, account.preClosurePenalInterestOnTypeOptions, + account.minDepositTerm, account.maxDepositTerm, account.minDepositTermType, account.maxDepositTermType, + account.inMultiplesOfDepositTerm, account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, + account.maturityDate, account.depositPeriod, account.depositPeriodFrequency, account.depositTermTypeOptions, + account.inMultiplesOfDepositTermTypeOptions, account.depositPeriodFrequencyOptions, account.depositType, + account.onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas); + } + + private FixedDepositAccountData(final Long id, final String accountNo, final String externalId, final Long groupId, + final String groupName, final Long clientId, final String clientName, final Long productId, final String productName, + final Long fieldofficerId, final String fieldofficerName, final SavingsAccountStatusEnumData status, + final SavingsAccountApplicationTimelineData timeline, final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, + final EnumOptionData interestPeriodType, final EnumOptionData interestPostingPeriodType, + final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType, + final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, + final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers, + final SavingsAccountSummaryData summary, final Collection transactions, + final Collection productOptions, final Collection fieldOfficerOptions, + final Collection interestCompoundingPeriodTypeOptions, + final Collection interestPostingPeriodTypeOptions, + final Collection interestCalculationTypeOptions, + final Collection interestCalculationDaysInYearTypeOptions, + final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, + final Collection charges, final Collection chargeOptions, + final DepositAccountInterestRateChartData accountChart, final DepositAccountInterestRateChartData chartTemplate, + final boolean interestFreePeriodApplicable, final Integer interestFreeFromPeriod, final Integer interestFreeToPeriod, + final EnumOptionData interestFreePeriodFrequencyType, final boolean preClosurePenalApplicable, + final BigDecimal preClosurePenalInterest, final EnumOptionData preClosurePenalInterestOnType, + final Collection interestFreePeriodTypeOptions, + final Collection preClosurePenalInterestOnTypeOptions, final Integer minDepositTerm, + final Integer maxDepositTerm, final EnumOptionData minDepositTermType, final EnumOptionData maxDepositTermType, + final Integer inMultiplesOfDepositTerm, final EnumOptionData inMultiplesOfDepositTermType, final BigDecimal depositAmount, + final BigDecimal maturityAmount, final LocalDate maturityDate, final Integer depositPeriod, + final EnumOptionData depositPeriodFrequency, final Collection depositTermTypeOptions, + final Collection inMultiplesOfDepositTermTypeOptions, + final Collection depositPeriodFrequencyOptions, final EnumOptionData depositType, + final EnumOptionData onAccountClosure, final Collection onAccountClosureOptions, + final Collection paymentTypeOptions, final Collection savingsAccountDatas) { + + super(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, fieldofficerId, + fieldofficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType, interestPostingPeriodType, + interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, + lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions, fieldOfficerOptions, + interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, + chargeOptions, accountChart, chartTemplate, depositType); + + // fixed deposit additional fields + this.interestFreePeriodApplicable = interestFreePeriodApplicable; + this.interestFreeFromPeriod = interestFreeFromPeriod; + this.interestFreeToPeriod = interestFreeToPeriod; + this.interestFreePeriodFrequencyType = interestFreePeriodFrequencyType; + this.preClosurePenalApplicable = preClosurePenalApplicable; + this.preClosurePenalInterest = preClosurePenalInterest; + this.preClosurePenalInterestOnType = preClosurePenalInterestOnType; + this.minDepositTerm = minDepositTerm; + this.maxDepositTerm = maxDepositTerm; + this.minDepositTermType = minDepositTermType; + this.maxDepositTermType = maxDepositTermType; + this.inMultiplesOfDepositTerm = inMultiplesOfDepositTerm; + this.inMultiplesOfDepositTermType = inMultiplesOfDepositTermType; + this.depositAmount = depositAmount; + this.maturityAmount = maturityAmount; + this.maturityDate = maturityDate; + this.depositPeriod = depositPeriod; + this.depositPeriodFrequency = depositPeriodFrequency; + this.onAccountClosure = onAccountClosure; + + // template + this.interestFreePeriodTypeOptions = interestFreePeriodTypeOptions; + this.preClosurePenalInterestOnTypeOptions = preClosurePenalInterestOnTypeOptions; + this.depositTermTypeOptions = depositTermTypeOptions; + this.inMultiplesOfDepositTermTypeOptions = inMultiplesOfDepositTermTypeOptions; + this.depositPeriodFrequencyOptions = depositPeriodFrequencyOptions; + + // account close template options + this.onAccountClosureOptions = onAccountClosureOptions; + this.paymentTypeOptions = paymentTypeOptions; + this.savingsAccounts = savingsAccountDatas; + } + + @Override + public boolean equals(final Object obj) { + + if (obj == null) { return false; } + if (obj == this) { return true; } + if (obj.getClass() != getClass()) { return false; } + final FixedDepositAccountData rhs = (FixedDepositAccountData) obj; + return new EqualsBuilder().append(this.id, rhs.id).append(this.accountNo, rhs.accountNo).isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37).append(this.id).append(this.accountNo).toHashCode(); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/FixedDepositProductData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/FixedDepositProductData.java new file mode 100644 index 000000000..48a859046 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/FixedDepositProductData.java @@ -0,0 +1,392 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.data; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.mifosplatform.accounting.glaccount.data.GLAccountData; +import org.mifosplatform.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper; +import org.mifosplatform.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper; +import org.mifosplatform.infrastructure.codes.data.CodeValueData; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.organisation.monetary.data.CurrencyData; +import org.mifosplatform.portfolio.charge.data.ChargeData; +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartData; + +/** + * Immutable data object representing a Fixed Deposit product. + */ +public class FixedDepositProductData extends DepositProductData { + + // additional fields + private boolean interestFreePeriodApplicable; + private Integer interestFreeFromPeriod; + private Integer interestFreeToPeriod; + protected EnumOptionData interestFreePeriodFrequencyType; + private boolean preClosurePenalApplicable; + protected BigDecimal preClosurePenalInterest; + protected EnumOptionData preClosurePenalInterestOnType; + protected Integer minDepositTerm; + protected Integer maxDepositTerm; + private EnumOptionData minDepositTermType; + private EnumOptionData maxDepositTermType; + protected Integer inMultiplesOfDepositTerm; + protected EnumOptionData inMultiplesOfDepositTermType; + + private Collection interestFreePeriodTypeOptions; + private Collection preClosurePenalInterestOnTypeOptions; + private Collection depositTermTypeOptions; + private Collection inMultiplesOfDepositTermTypeOptions; + + public static FixedDepositProductData template(final CurrencyData currency, final EnumOptionData interestCompoundingPeriodType, + final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType, + final EnumOptionData interestCalculationDaysInYearType, final EnumOptionData accountingRule, + final Collection currencyOptions, final Collection interestCompoundingPeriodTypeOptions, + final Collection interestPostingPeriodTypeOptions, + final Collection interestCalculationTypeOptions, + final Collection interestCalculationDaysInYearTypeOptions, + final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, + final Collection paymentTypeOptions, final Collection accountingRuleOptions, + final Map> accountingMappingOptions, final Collection chargeOptions, + final Collection penaltyOptions, final InterestRateChartData chartTemplate, + final Collection interestFreePeriodTypes, + final Collection preClosurePenalInterestOnTypeOptions, final Collection depositTermTypeOptions, + final Collection inMultiplesOfDepositTermTypeOptions) { + + final Long id = null; + final String name = null; + final String shortName = null; + final String description = null; + final BigDecimal nominalAnnualInterestRate = null; + final Integer lockinPeriodFrequency = null; + final EnumOptionData lockinPeriodFrequencyType = null; + final Map accountingMappings = null; + final Collection paymentChannelToFundSourceMappings = null; + final Collection charges = null; + final Collection feeToIncomeAccountMappings = null; + final Collection penaltyToIncomeAccountMappings = null; + final Collection interestRateCharts = null; + final boolean interestFreePeriodApplicable = false; + final Integer interestFreeFromPeriod = null; + final Integer interestFreeToPeriod = null; + final EnumOptionData interestFreePeriodFrequencyType = null; + final boolean preClosurePenalApplicable = false; + final BigDecimal preClosurePenalInterest = null; + final EnumOptionData preClosurePenalInterestOnType = null; + final Integer minDepositTerm = null; + final Integer maxDepositTerm = null; + final EnumOptionData minDepositTermType = null; + final EnumOptionData maxDepositTermType = null; + final Integer inMultiplesOfDepositTerm = null; + final EnumOptionData inMultiplesOfDepositTermType = null; + + return new FixedDepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, + interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, + lockinPeriodFrequency, lockinPeriodFrequencyType, accountingRule, accountingMappings, paymentChannelToFundSourceMappings, + currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, + accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, + penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, interestFreePeriodApplicable, interestFreeFromPeriod, + interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest, + preClosurePenalInterestOnType, interestFreePeriodTypes, preClosurePenalInterestOnTypeOptions, minDepositTerm, + maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositTermTypeOptions, + inMultiplesOfDepositTermTypeOptions); + } + + public static FixedDepositProductData withCharges(final FixedDepositProductData existingProduct, final Collection charges) { + return new FixedDepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, + existingProduct.description, existingProduct.currency, existingProduct.nominalAnnualInterestRate, + existingProduct.interestCompoundingPeriodType, existingProduct.interestPostingPeriodType, + existingProduct.interestCalculationType, existingProduct.interestCalculationDaysInYearType, + existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType, existingProduct.accountingRule, + existingProduct.accountingMappings, existingProduct.paymentChannelToFundSourceMappings, existingProduct.currencyOptions, + existingProduct.interestCompoundingPeriodTypeOptions, existingProduct.interestPostingPeriodTypeOptions, + existingProduct.interestCalculationTypeOptions, existingProduct.interestCalculationDaysInYearTypeOptions, + existingProduct.lockinPeriodFrequencyTypeOptions, existingProduct.withdrawalFeeTypeOptions, + existingProduct.paymentTypeOptions, existingProduct.accountingRuleOptions, existingProduct.accountingMappingOptions, + charges, existingProduct.chargeOptions, existingProduct.penaltyOptions, existingProduct.feeToIncomeAccountMappings, + existingProduct.penaltyToIncomeAccountMappings, existingProduct.interestRateCharts, existingProduct.chartTemplate, + existingProduct.interestFreePeriodApplicable, existingProduct.interestFreeFromPeriod, existingProduct.interestFreeToPeriod, + existingProduct.interestFreePeriodFrequencyType, existingProduct.preClosurePenalApplicable, + existingProduct.preClosurePenalInterest, existingProduct.preClosurePenalInterestOnType, + existingProduct.interestFreePeriodTypeOptions, existingProduct.preClosurePenalInterestOnTypeOptions, + existingProduct.minDepositTerm, existingProduct.maxDepositTerm, existingProduct.minDepositTermType, + existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm, existingProduct.inMultiplesOfDepositTermType, + existingProduct.depositTermTypeOptions, existingProduct.inMultiplesOfDepositTermTypeOptions); + } + + /** + * Returns a {@link FixedDepositProductData} that contains and exist + * {@link FixedDepositProductData} data with further template data for + * dropdowns. + */ + public static FixedDepositProductData withTemplate(final FixedDepositProductData existingProduct, + final Collection currencyOptions, final Collection interestCompoundingPeriodTypeOptions, + final Collection interestPostingPeriodTypeOptions, + final Collection interestCalculationTypeOptions, + final Collection interestCalculationDaysInYearTypeOptions, + final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, + final Collection paymentTypeOptions, final Collection accountingRuleOptions, + final Map> accountingMappingOptions, final Collection chargeOptions, + final Collection penaltyOptions, final InterestRateChartData chartTemplate, + final Collection interestFreePeriodTypes, + final Collection preClosurePenalInterestOnTypeOptions, final Collection depositTermTypeOptions, + final Collection inMultiplesOfDepositTermTypeOptions) { + + return new FixedDepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, + existingProduct.description, existingProduct.currency, existingProduct.nominalAnnualInterestRate, + existingProduct.interestCompoundingPeriodType, existingProduct.interestPostingPeriodType, + existingProduct.interestCalculationType, existingProduct.interestCalculationDaysInYearType, + existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType, existingProduct.accountingRule, + existingProduct.accountingMappings, existingProduct.paymentChannelToFundSourceMappings, currencyOptions, + interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, + accountingRuleOptions, accountingMappingOptions, existingProduct.charges, chargeOptions, penaltyOptions, + existingProduct.feeToIncomeAccountMappings, existingProduct.penaltyToIncomeAccountMappings, + existingProduct.interestRateCharts, chartTemplate, existingProduct.interestFreePeriodApplicable, + existingProduct.interestFreeFromPeriod, existingProduct.interestFreeToPeriod, + existingProduct.interestFreePeriodFrequencyType, existingProduct.preClosurePenalApplicable, + existingProduct.preClosurePenalInterest, existingProduct.preClosurePenalInterestOnType, interestFreePeriodTypes, + preClosurePenalInterestOnTypeOptions, existingProduct.minDepositTerm, existingProduct.maxDepositTerm, + existingProduct.minDepositTermType, existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm, + existingProduct.inMultiplesOfDepositTermType, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions); + } + + public static FixedDepositProductData withAccountingDetails(final FixedDepositProductData existingProduct, + final Map accountingMappings, + final Collection paymentChannelToFundSourceMappings, + final Collection feeToIncomeAccountMappings, + final Collection penaltyToIncomeAccountMappings) { + + final Collection currencyOptions = null; + final Collection interestCompoundingPeriodTypeOptions = null; + final Collection interestPostingPeriodTypeOptions = null; + final Collection interestCalculationTypeOptions = null; + final Collection interestCalculationDaysInYearTypeOptions = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + final Collection paymentTypeOptions = null; + final Collection accountingRuleOptions = null; + final Map> accountingMappingOptions = null; + final Collection chargeOptions = null; + final Collection penaltyOptions = null; + + return new FixedDepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, + existingProduct.description, existingProduct.currency, existingProduct.nominalAnnualInterestRate, + existingProduct.interestCompoundingPeriodType, existingProduct.interestPostingPeriodType, + existingProduct.interestCalculationType, existingProduct.interestCalculationDaysInYearType, + existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType, existingProduct.accountingRule, + accountingMappings, paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions, + interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, + lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, + accountingMappingOptions, existingProduct.charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, + penaltyToIncomeAccountMappings, existingProduct.interestRateCharts, existingProduct.chartTemplate, + existingProduct.interestFreePeriodApplicable, existingProduct.interestFreeFromPeriod, existingProduct.interestFreeToPeriod, + existingProduct.interestFreePeriodFrequencyType, existingProduct.preClosurePenalApplicable, + existingProduct.preClosurePenalInterest, existingProduct.preClosurePenalInterestOnType, + existingProduct.interestFreePeriodTypeOptions, existingProduct.preClosurePenalInterestOnTypeOptions, + existingProduct.minDepositTerm, existingProduct.maxDepositTerm, existingProduct.minDepositTermType, + existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm, existingProduct.inMultiplesOfDepositTermType, + existingProduct.depositTermTypeOptions, existingProduct.inMultiplesOfDepositTermTypeOptions); + } + + public static FixedDepositProductData instance(final DepositProductData depositProductData, final boolean interestFreePeriodApplicable, + final Integer interestFreeFromPeriod, final Integer interestFreeToPeriod, final EnumOptionData interestFreePeriodFrequencyType, + final boolean preClosurePenalApplicable, final BigDecimal preClosurePenalInterest, + final EnumOptionData preClosurePenalInterestOnType, final Integer minDepositTerm, final Integer maxDepositTerm, + final EnumOptionData minDepositTermType, final EnumOptionData maxDepositTermType, final Integer inMultiplesOfDepositTerm, + final EnumOptionData inMultiplesOfDepositTermType) { + + final Map accountingMappings = null; + final Collection paymentChannelToFundSourceMappings = null; + + final Collection currencyOptions = null; + final Collection interestCompoundingPeriodTypeOptions = null; + final Collection interestPostingPeriodTypeOptions = null; + final Collection interestCalculationTypeOptions = null; + final Collection interestCalculationDaysInYearTypeOptions = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + final Collection paymentTypeOptions = null; + final Collection accountingRuleOptions = null; + final Map> accountingMappingOptions = null; + final Collection chargeOptions = null; + final Collection penaltyOptions = null; + final Collection charges = null; + final Collection feeToIncomeAccountMappings = null; + final Collection penaltyToIncomeAccountMappings = null; + final Collection interestRateCharts = null; + final InterestRateChartData chartTemplate = null; + final Collection interestFreePeriodTypes = null; + final Collection preClosurePenalInterestOnTypeOptions = null; + final Collection depositTermTypeOptions = null; + final Collection inMultiplesOfDepositTermTypeOptions = null; + + return new FixedDepositProductData(depositProductData.id, depositProductData.name, depositProductData.shortName, + depositProductData.description, depositProductData.currency, depositProductData.nominalAnnualInterestRate, + depositProductData.interestCompoundingPeriodType, depositProductData.interestPostingPeriodType, + depositProductData.interestCalculationType, depositProductData.interestCalculationDaysInYearType, + depositProductData.lockinPeriodFrequency, depositProductData.lockinPeriodFrequencyType, depositProductData.accountingRule, + accountingMappings, paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions, + interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, + lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, + accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, + penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, interestFreePeriodApplicable, interestFreeFromPeriod, + interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest, + preClosurePenalInterestOnType, interestFreePeriodTypes, preClosurePenalInterestOnTypeOptions, minDepositTerm, + maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, + depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions); + } + + public static FixedDepositProductData lookup(final Long id, final String name) { + + final String shortName = null; + final CurrencyData currency = null; + final String description = null; + final BigDecimal nominalAnnualInterestRate = null; + final EnumOptionData interestCompoundingPeriodType = null; + final EnumOptionData interestPostingPeriodType = null; + final EnumOptionData interestCalculationType = null; + final EnumOptionData interestCalculationDaysInYearType = null; + final Integer lockinPeriodFrequency = null; + final EnumOptionData lockinPeriodFrequencyType = null; + final EnumOptionData accountingType = null; + final Map accountingMappings = null; + final Collection paymentChannelToFundSourceMappings = null; + + final Collection currencyOptions = null; + final Collection interestCompoundingPeriodTypeOptions = null; + final Collection interestPostingPeriodTypeOptions = null; + final Collection interestCalculationTypeOptions = null; + final Collection interestCalculationDaysInYearTypeOptions = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + final Collection paymentTypeOptions = null; + final Collection accountingRuleOptions = null; + final Map> accountingMappingOptions = null; + final Collection charges = null; + final Collection chargeOptions = null; + final Collection penaltyOptions = null; + final Collection feeToIncomeAccountMappings = null; + final Collection penaltyToIncomeAccountMappings = null; + final Collection interestRateCharts = null; + final InterestRateChartData chartTemplate = null; + final boolean interestFreePeriodApplicable = false; + final Integer interestFreeFromPeriod = null; + final Integer interestFreeToPeriod = null; + final EnumOptionData interestFreePeriodFrequencyType = null; + final boolean preClosurePenalApplicable = false; + final BigDecimal preClosurePenalInterest = null; + final EnumOptionData preClosurePenalInterestOnType = null; + final Collection interestFreePeriodTypes = null; + final Collection preClosurePenalInterestOnTypeOptions = null; + final Integer minDepositTerm = null; + final Integer maxDepositTerm = null; + final EnumOptionData minDepositTermType = null; + final EnumOptionData maxDepositTermType = null; + final Integer inMultiplesOfDepositTerm = null; + final EnumOptionData inMultiplesOfDepositTermType = null; + final Collection depositTermTypeOptions = null; + final Collection inMultiplesOfDepositTermTypeOptions = null; + + return new FixedDepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, + interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, + lockinPeriodFrequency, lockinPeriodFrequencyType, accountingType, accountingMappings, paymentChannelToFundSourceMappings, + currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, + accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, + penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, interestFreePeriodApplicable, interestFreeFromPeriod, + interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest, + preClosurePenalInterestOnType, interestFreePeriodTypes, preClosurePenalInterestOnTypeOptions, minDepositTerm, + maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, + depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions); + } + + public static FixedDepositProductData withInterestChart(final FixedDepositProductData existingProduct, + final Collection interestRateCharts) { + return new FixedDepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, + existingProduct.description, existingProduct.currency, existingProduct.nominalAnnualInterestRate, + existingProduct.interestCompoundingPeriodType, existingProduct.interestPostingPeriodType, + existingProduct.interestCalculationType, existingProduct.interestCalculationDaysInYearType, + existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType, existingProduct.accountingRule, + existingProduct.accountingMappings, existingProduct.paymentChannelToFundSourceMappings, existingProduct.currencyOptions, + existingProduct.interestCompoundingPeriodTypeOptions, existingProduct.interestPostingPeriodTypeOptions, + existingProduct.interestCalculationTypeOptions, existingProduct.interestCalculationDaysInYearTypeOptions, + existingProduct.lockinPeriodFrequencyTypeOptions, existingProduct.withdrawalFeeTypeOptions, + existingProduct.paymentTypeOptions, existingProduct.accountingRuleOptions, existingProduct.accountingMappingOptions, + existingProduct.charges, existingProduct.chargeOptions, existingProduct.penaltyOptions, + existingProduct.feeToIncomeAccountMappings, existingProduct.penaltyToIncomeAccountMappings, interestRateCharts, + existingProduct.chartTemplate, existingProduct.interestFreePeriodApplicable, existingProduct.interestFreeFromPeriod, + existingProduct.interestFreeToPeriod, existingProduct.interestFreePeriodFrequencyType, + existingProduct.preClosurePenalApplicable, existingProduct.preClosurePenalInterest, + existingProduct.preClosurePenalInterestOnType, existingProduct.interestFreePeriodTypeOptions, + existingProduct.preClosurePenalInterestOnTypeOptions, existingProduct.minDepositTerm, existingProduct.maxDepositTerm, + existingProduct.minDepositTermType, existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm, + existingProduct.inMultiplesOfDepositTermType, existingProduct.depositTermTypeOptions, + existingProduct.inMultiplesOfDepositTermTypeOptions); + } + + private FixedDepositProductData(final Long id, final String name, final String shortName, final String description, + final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, final EnumOptionData interestCompoundingPeriodType, + final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType, + final EnumOptionData interestCalculationDaysInYearType, final Integer lockinPeriodFrequency, + final EnumOptionData lockinPeriodFrequencyType, final EnumOptionData accountingType, + final Map accountingMappings, + final Collection paymentChannelToFundSourceMappings, + final Collection currencyOptions, final Collection interestCompoundingPeriodTypeOptions, + final Collection interestPostingPeriodTypeOptions, + final Collection interestCalculationTypeOptions, + final Collection interestCalculationDaysInYearTypeOptions, + final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, + final Collection paymentTypeOptions, final Collection accountingRuleOptions, + final Map> accountingMappingOptions, final Collection charges, + final Collection chargeOptions, final Collection penaltyOptions, + final Collection feeToIncomeAccountMappings, + final Collection penaltyToIncomeAccountMappings, + final Collection interestRateCharts, final InterestRateChartData chartTemplate, + final boolean interestFreePeriodApplicable, final Integer interestFreeFromPeriod, final Integer interestFreeToPeriod, + final EnumOptionData interestFreePeriodFrequencyType, final boolean preClosurePenalApplicable, + final BigDecimal preClosurePenalInterest, final EnumOptionData preClosurePenalInterestOnType, + final Collection interestFreePeriodTypeOptions, + final Collection preClosurePenalInterestOnTypeOptions, final Integer minDepositTerm, + final Integer maxDepositTerm, final EnumOptionData minDepositTermType, final EnumOptionData maxDepositTermType, + final Integer inMultiplesOfDepositTerm, final EnumOptionData inMultiplesOfDepositTermType, + final Collection depositTermTypeOptions, Collection inMultiplesOfDepositTermTypeOptions) { + + super(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, + interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, lockinPeriodFrequency, + lockinPeriodFrequencyType, accountingType, accountingMappings, paymentChannelToFundSourceMappings, currencyOptions, + interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, + accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, + penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate); + + // fixed deposit additional fields + this.interestFreePeriodApplicable = interestFreePeriodApplicable; + this.interestFreeFromPeriod = interestFreeFromPeriod; + this.interestFreeToPeriod = interestFreeToPeriod; + this.interestFreePeriodFrequencyType = interestFreePeriodFrequencyType; + this.preClosurePenalApplicable = preClosurePenalApplicable; + this.preClosurePenalInterest = preClosurePenalInterest; + this.preClosurePenalInterestOnType = preClosurePenalInterestOnType; + this.minDepositTerm = minDepositTerm; + this.maxDepositTerm = maxDepositTerm; + this.minDepositTermType = minDepositTermType; + this.maxDepositTermType = maxDepositTermType; + this.inMultiplesOfDepositTerm = inMultiplesOfDepositTerm; + this.inMultiplesOfDepositTermType = inMultiplesOfDepositTermType; + + // template + this.interestFreePeriodTypeOptions = interestFreePeriodTypeOptions; + this.preClosurePenalInterestOnTypeOptions = preClosurePenalInterestOnTypeOptions; + this.depositTermTypeOptions = depositTermTypeOptions; + this.inMultiplesOfDepositTermTypeOptions = inMultiplesOfDepositTermTypeOptions; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/RecurringDepositAccountData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/RecurringDepositAccountData.java new file mode 100644 index 000000000..13b3cdb1c --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/RecurringDepositAccountData.java @@ -0,0 +1,536 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.data; + +import java.math.BigDecimal; +import java.util.Collection; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.codes.data.CodeValueData; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.organisation.monetary.data.CurrencyData; +import org.mifosplatform.organisation.staff.data.StaffData; +import org.mifosplatform.portfolio.charge.data.ChargeData; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.service.SavingsEnumerations; + +/** + * Immutable data object representing a Recurring Deposit account. + */ +public class RecurringDepositAccountData extends DepositAccountData { + + // additional fields + private boolean interestFreePeriodApplicable; + private Integer interestFreeFromPeriod; + private Integer interestFreeToPeriod; + private EnumOptionData interestFreePeriodFrequencyType; + private boolean preClosurePenalApplicable; + private BigDecimal preClosurePenalInterest; + private EnumOptionData preClosurePenalInterestOnType; + private Integer minDepositTerm; + private Integer maxDepositTerm; + private EnumOptionData minDepositTermType; + private EnumOptionData maxDepositTermType; + private Integer inMultiplesOfDepositTerm; + private EnumOptionData inMultiplesOfDepositTermType; + private BigDecimal depositAmount; + private BigDecimal maturityAmount; + private LocalDate maturityDate; + private Integer depositPeriod; + private EnumOptionData depositPeriodFrequency; + private BigDecimal recurringDepositAmount; + private EnumOptionData recurringDepositType; + private Integer recurringDepositFrequency; + private EnumOptionData recurringDepositFrequencyType; + + // used for account close + private EnumOptionData onAccountClosure; + + private Collection interestFreePeriodTypeOptions; + private Collection preClosurePenalInterestOnTypeOptions; + private Collection recurringDepositTypeOptions; + private Collection recurringDepositFrequencyTypeOptions; + private Collection depositTermTypeOptions; + private Collection inMultiplesOfDepositTermTypeOptions; + private Collection depositPeriodFrequencyOptions; + private Collection savingsAccounts; + + // for account close + private Collection onAccountClosureOptions; + private Collection paymentTypeOptions; + + public static RecurringDepositAccountData instance(final DepositAccountData depositAccountData, + final boolean interestFreePeriodApplicable, final Integer interestFreeFromPeriod, final Integer interestFreeToPeriod, + final EnumOptionData interestFreePeriodFrequencyType, final boolean preClosurePenalApplicable, + final BigDecimal preClosurePenalInterest, final EnumOptionData preClosurePenalInterestOnType, final Integer minDepositTerm, + final Integer maxDepositTerm, final EnumOptionData minDepositTermType, final EnumOptionData maxDepositTermType, + final Integer inMultiplesOfDepositTerm, final EnumOptionData inMultiplesOfDepositTermType, final BigDecimal depositAmount, + final BigDecimal maturityAmount, final LocalDate maturityDate, final Integer depositPeriod, + final EnumOptionData depositPeriodFrequency, final BigDecimal recurringDepositAmount, + final EnumOptionData recurringDepositType, final Integer recurringDepositFrequency, + final EnumOptionData recurringDepositFrequencyType, final EnumOptionData onAccountClosure) { + + final Collection interestFreePeriodTypeOptions = null; + final Collection preClosurePenalInterestOnTypeOptions = null; + final Collection depositTermTypeOptions = null; + final Collection inMultiplesOfDepositTermTypeOptions = null; + final Collection recurringDepositTypeOptions = null; + final Collection recurringDepositFrequencyTypeOptions = null; + final Collection depositPeriodFrequencyOptions = null; + final EnumOptionData depositType = SavingsEnumerations.depositType(DepositAccountType.FIXED_DEPOSIT.getValue()); + final Collection onAccountClosureOptions = null; + final Collection paymentTypeOptions = null; + final Collection savingsAccountDatas = null; + + return new RecurringDepositAccountData(depositAccountData.id, depositAccountData.accountNo, depositAccountData.externalId, + depositAccountData.groupId, depositAccountData.groupName, depositAccountData.clientId, depositAccountData.clientName, + depositAccountData.savingsProductId, depositAccountData.savingsProductName, depositAccountData.fieldOfficerId, + depositAccountData.fieldOfficerName, depositAccountData.status, depositAccountData.timeline, depositAccountData.currency, + depositAccountData.nominalAnnualInterestRate, depositAccountData.interestCompoundingPeriodType, + depositAccountData.interestPostingPeriodType, depositAccountData.interestCalculationType, + depositAccountData.interestCalculationDaysInYearType, depositAccountData.minRequiredOpeningBalance, + depositAccountData.lockinPeriodFrequency, depositAccountData.lockinPeriodFrequencyType, + depositAccountData.withdrawalFeeForTransfers, depositAccountData.summary, depositAccountData.transactions, + depositAccountData.productOptions, depositAccountData.fieldOfficerOptions, + depositAccountData.interestCompoundingPeriodTypeOptions, depositAccountData.interestPostingPeriodTypeOptions, + depositAccountData.interestCalculationTypeOptions, depositAccountData.interestCalculationDaysInYearTypeOptions, + depositAccountData.lockinPeriodFrequencyTypeOptions, depositAccountData.withdrawalFeeTypeOptions, + depositAccountData.charges, depositAccountData.chargeOptions, depositAccountData.accountChart, + depositAccountData.chartTemplate, interestFreePeriodApplicable, interestFreeFromPeriod, interestFreeToPeriod, + interestFreePeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest, preClosurePenalInterestOnType, + interestFreePeriodTypeOptions, preClosurePenalInterestOnTypeOptions, minDepositTerm, maxDepositTerm, minDepositTermType, + maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, + depositPeriod, depositPeriodFrequency, recurringDepositAmount, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions, + recurringDepositType, recurringDepositFrequency, recurringDepositFrequencyType, recurringDepositTypeOptions, + recurringDepositFrequencyTypeOptions, depositPeriodFrequencyOptions, depositType, onAccountClosure, + onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas); + } + + public static RecurringDepositAccountData withInterestChart(final RecurringDepositAccountData account, + final DepositAccountInterestRateChartData accountChart) { + return new RecurringDepositAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName, + account.clientId, account.clientName, account.savingsProductId, account.savingsProductName, account.fieldOfficerId, + account.fieldOfficerName, account.status, account.timeline, account.currency, account.nominalAnnualInterestRate, + account.interestCompoundingPeriodType, account.interestPostingPeriodType, account.interestCalculationType, + account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency, + account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers, account.summary, account.transactions, + account.productOptions, account.fieldOfficerOptions, account.interestCompoundingPeriodTypeOptions, + account.interestPostingPeriodTypeOptions, account.interestCalculationTypeOptions, + account.interestCalculationDaysInYearTypeOptions, account.lockinPeriodFrequencyTypeOptions, + account.withdrawalFeeTypeOptions, account.charges, account.chargeOptions, accountChart, account.chartTemplate, + account.interestFreePeriodApplicable, account.interestFreeFromPeriod, account.interestFreeToPeriod, + account.interestFreePeriodFrequencyType, account.preClosurePenalApplicable, account.preClosurePenalInterest, + account.preClosurePenalInterestOnType, account.interestFreePeriodTypeOptions, account.preClosurePenalInterestOnTypeOptions, + account.minDepositTerm, account.maxDepositTerm, account.minDepositTermType, account.maxDepositTermType, + account.inMultiplesOfDepositTerm, account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, + account.maturityDate, account.depositPeriod, account.depositPeriodFrequency, account.recurringDepositAmount, + account.depositTermTypeOptions, account.inMultiplesOfDepositTermTypeOptions, account.recurringDepositType, + account.recurringDepositFrequency, account.recurringDepositFrequencyType, account.recurringDepositTypeOptions, + account.recurringDepositFrequencyTypeOptions, account.depositPeriodFrequencyOptions, account.depositType, + account.onAccountClosure, account.onAccountClosureOptions, account.paymentTypeOptions, account.savingsAccounts); + } + + public static RecurringDepositAccountData withTemplateOptions(final RecurringDepositAccountData account, + final RecurringDepositAccountData template, final Collection transactions, + final Collection charges) { + + if (template == null) { + final Collection productOptions = null; + final Collection fieldOfficerOptions = null; + final Collection interestCompoundingPeriodTypeOptions = null; + final Collection interestPostingPeriodTypeOptions = null; + final Collection interestCalculationTypeOptions = null; + final Collection interestCalculationDaysInYearTypeOptions = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + final Collection chargeOptions = null; + + final Collection interestFreePeriodTypeOptions = null; + final Collection preClosurePenalInterestOnTypeOptions = null; + final Collection depositTermTypeOptions = null; + final Collection inMultiplesOfDepositTermTypeOptions = null; + final Collection recurringDepositTypeOptions = null; + final Collection recurringDepositFrequencyTypeOptions = null; + final Collection depositPeriodFrequencyOptions = null; + + return withTemplateOptions(account, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions, + interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, + lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions, charges, chargeOptions, + interestFreePeriodTypeOptions, preClosurePenalInterestOnTypeOptions, depositTermTypeOptions, + inMultiplesOfDepositTermTypeOptions, recurringDepositTypeOptions, recurringDepositFrequencyTypeOptions, + depositPeriodFrequencyOptions); + } + + return new RecurringDepositAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName, + account.clientId, account.clientName, account.savingsProductId, account.savingsProductName, account.fieldOfficerId, + account.fieldOfficerName, account.status, account.timeline, account.currency, account.nominalAnnualInterestRate, + account.interestCompoundingPeriodType, account.interestPostingPeriodType, account.interestCalculationType, + account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency, + account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers, account.summary, transactions, + template.productOptions, template.fieldOfficerOptions, template.interestCompoundingPeriodTypeOptions, + template.interestPostingPeriodTypeOptions, template.interestCalculationTypeOptions, + template.interestCalculationDaysInYearTypeOptions, template.lockinPeriodFrequencyTypeOptions, + template.withdrawalFeeTypeOptions, charges, template.chargeOptions, account.accountChart, account.chartTemplate, + account.interestFreePeriodApplicable, account.interestFreeFromPeriod, account.interestFreeToPeriod, + account.interestFreePeriodFrequencyType, account.preClosurePenalApplicable, account.preClosurePenalInterest, + account.preClosurePenalInterestOnType, template.interestFreePeriodTypeOptions, + template.preClosurePenalInterestOnTypeOptions, account.minDepositTerm, account.maxDepositTerm, account.minDepositTermType, + account.maxDepositTermType, account.inMultiplesOfDepositTerm, account.inMultiplesOfDepositTermType, account.depositAmount, + account.maturityAmount, account.maturityDate, account.depositPeriod, account.depositPeriodFrequency, + account.recurringDepositAmount, template.depositTermTypeOptions, template.inMultiplesOfDepositTermTypeOptions, + account.recurringDepositType, account.recurringDepositFrequency, account.recurringDepositFrequencyType, + template.recurringDepositTypeOptions, template.recurringDepositFrequencyTypeOptions, + template.depositPeriodFrequencyOptions, account.depositType, account.onAccountClosure, account.onAccountClosureOptions, + account.paymentTypeOptions, account.savingsAccounts); + + } + + public static RecurringDepositAccountData withTemplateOptions(final RecurringDepositAccountData account, + final Collection productOptions, final Collection fieldOfficerOptions, + final Collection interestCompoundingPeriodTypeOptions, + final Collection interestPostingPeriodTypeOptions, + final Collection interestCalculationTypeOptions, + final Collection interestCalculationDaysInYearTypeOptions, + final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, + final Collection transactions, final Collection charges, + final Collection chargeOptions, final Collection interestFreePeriodTypeOptions, + final Collection preClosurePenalInterestOnTypeOptions, final Collection depositTermTypeOptions, + final Collection inMultiplesOfDepositTermTypeOptions, + final Collection recurringDepositTypeOptions, + final Collection recurringDepositFrequencyTypeOptions, + final Collection depositPeriodFrequencyOptions) { + + return new RecurringDepositAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName, + account.clientId, account.clientName, account.savingsProductId, account.savingsProductName, account.fieldOfficerId, + account.fieldOfficerName, account.status, account.timeline, account.currency, account.nominalAnnualInterestRate, + account.interestCompoundingPeriodType, account.interestPostingPeriodType, account.interestCalculationType, + account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency, + account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers, account.summary, transactions, productOptions, + fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, + interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, + withdrawalFeeTypeOptions, charges, chargeOptions, account.accountChart, account.chartTemplate, + account.interestFreePeriodApplicable, account.interestFreeFromPeriod, account.interestFreeToPeriod, + account.interestFreePeriodFrequencyType, account.preClosurePenalApplicable, account.preClosurePenalInterest, + account.preClosurePenalInterestOnType, interestFreePeriodTypeOptions, preClosurePenalInterestOnTypeOptions, + account.minDepositTerm, account.maxDepositTerm, account.minDepositTermType, account.maxDepositTermType, + account.inMultiplesOfDepositTerm, account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, + account.maturityDate, account.depositPeriod, account.depositPeriodFrequency, account.recurringDepositAmount, + depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions, account.recurringDepositType, + account.recurringDepositFrequency, account.recurringDepositFrequencyType, recurringDepositTypeOptions, + recurringDepositFrequencyTypeOptions, depositPeriodFrequencyOptions, account.depositType, account.onAccountClosure, + account.onAccountClosureOptions, account.paymentTypeOptions, account.savingsAccounts); + } + + public static RecurringDepositAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId, + final String groupName) { + + final Long id = null; + final String accountNo = null; + final String externalId = null; + final Long productId = null; + final String productName = null; + final Long fieldOfficerId = null; + final String fieldOfficerName = null; + final SavingsAccountStatusEnumData status = null; + final SavingsAccountApplicationTimelineData timeline = null; + final CurrencyData currency = null; + final BigDecimal nominalAnnualInterestRate = null; + final EnumOptionData interestPeriodType = null; + final EnumOptionData interestPostingPeriodType = null; + final EnumOptionData interestCalculationType = null; + final EnumOptionData interestCalculationDaysInYearType = null; + final BigDecimal minRequiredOpeningBalance = null; + final Integer lockinPeriodFrequency = null; + final EnumOptionData lockinPeriodFrequencyType = null; + final boolean withdrawalFeeForTransfers = false; + final SavingsAccountSummaryData summary = null; + final Collection transactions = null; + + final Collection productOptions = null; + final Collection fieldOfficerOptions = null; + final Collection interestCompoundingPeriodTypeOptions = null; + final Collection interestPostingPeriodTypeOptions = null; + final Collection interestCalculationTypeOptions = null; + final Collection interestCalculationDaysInYearTypeOptions = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + + final Collection charges = null; + final Collection chargeOptions = null; + + final DepositAccountInterestRateChartData accountChart = null; + final DepositAccountInterestRateChartData chartTemplate = null; + final boolean interestFreePeriodApplicable = false; + final Integer interestFreeFromPeriod = null; + final Integer interestFreeToPeriod = null; + final EnumOptionData interestFreePeriodFrequencyType = null; + final boolean preClosurePenalApplicable = false; + final BigDecimal preClosurePenalInterest = null; + final EnumOptionData preClosurePenalInterestOnType = null; + final Integer minDepositTerm = null; + final Integer maxDepositTerm = null; + final EnumOptionData minDepositTermType = null; + final EnumOptionData maxDepositTermType = null; + final Integer inMultiplesOfDepositTerm = null; + final EnumOptionData inMultiplesOfDepositTermType = null; + final BigDecimal depositAmount = null; + final BigDecimal maturityAmount = null; + final LocalDate maturityDate = null; + final Integer depositPeriod = null; + final EnumOptionData depositPeriodFrequency = null; + final BigDecimal recurringDepositAmount = null; + final EnumOptionData onAccountClosure = null; + + final Collection interestFreePeriodTypeOptions = null; + final Collection preClosurePenalInterestOnTypeOptions = null; + final Collection depositTermTypeOptions = null; + final Collection inMultiplesOfDepositTermTypeOptions = null; + + final EnumOptionData recurringDepositType = null; + final Integer recurringDepositFrequency = null; + final EnumOptionData recurringDepositFrequencyType = null; + final Collection recurringDepositTypeOptions = null; + final Collection recurringDepositFrequencyTypeOptions = null; + final Collection depositPeriodFrequencyOptions = null; + final EnumOptionData depositType = SavingsEnumerations.depositType(DepositAccountType.RECURRING_DEPOSIT.getValue()); + final Collection onAccountClosureOptions = null; + final Collection paymentTypeOptions = null; + final Collection savingsAccountDatas = null; + + return new RecurringDepositAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, + fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType, + interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions, + fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, + interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, + withdrawalFeeTypeOptions, charges, chargeOptions, accountChart, chartTemplate, interestFreePeriodApplicable, + interestFreeFromPeriod, interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, + preClosurePenalInterest, preClosurePenalInterestOnType, interestFreePeriodTypeOptions, + preClosurePenalInterestOnTypeOptions, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, + inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod, + depositPeriodFrequency, recurringDepositAmount, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions, + recurringDepositType, recurringDepositFrequency, recurringDepositFrequencyType, recurringDepositTypeOptions, + recurringDepositFrequencyTypeOptions, depositPeriodFrequencyOptions, depositType, onAccountClosure, + onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas); + } + + public static RecurringDepositAccountData preClosureDetails(final Long accountId, BigDecimal maturityAmount, + final Collection onAccountClosureOptions, final Collection paymentTypeOptions, final Collection savingsAccountDatas) { + + final String accountNo = null; + final String externalId = null; + final Long productId = null; + final String productName = null; + final Long fieldOfficerId = null; + final String fieldOfficerName = null; + final SavingsAccountStatusEnumData status = null; + final SavingsAccountApplicationTimelineData timeline = null; + final CurrencyData currency = null; + final BigDecimal nominalAnnualInterestRate = null; + final EnumOptionData interestPeriodType = null; + final EnumOptionData interestPostingPeriodType = null; + final EnumOptionData interestCalculationType = null; + final EnumOptionData interestCalculationDaysInYearType = null; + final BigDecimal minRequiredOpeningBalance = null; + final Integer lockinPeriodFrequency = null; + final EnumOptionData lockinPeriodFrequencyType = null; + final boolean withdrawalFeeForTransfers = false; + final SavingsAccountSummaryData summary = null; + final Collection transactions = null; + + final Collection productOptions = null; + final Collection fieldOfficerOptions = null; + final Collection interestCompoundingPeriodTypeOptions = null; + final Collection interestPostingPeriodTypeOptions = null; + final Collection interestCalculationTypeOptions = null; + final Collection interestCalculationDaysInYearTypeOptions = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + + final Collection charges = null; + final Collection chargeOptions = null; + + final DepositAccountInterestRateChartData accountChart = null; + final DepositAccountInterestRateChartData chartTemplate = null; + final boolean interestFreePeriodApplicable = false; + final Integer interestFreeFromPeriod = null; + final Integer interestFreeToPeriod = null; + final EnumOptionData interestFreePeriodFrequencyType = null; + final boolean preClosurePenalApplicable = false; + final BigDecimal preClosurePenalInterest = null; + final EnumOptionData preClosurePenalInterestOnType = null; + final Integer minDepositTerm = null; + final Integer maxDepositTerm = null; + final EnumOptionData minDepositTermType = null; + final EnumOptionData maxDepositTermType = null; + final Integer inMultiplesOfDepositTerm = null; + final EnumOptionData inMultiplesOfDepositTermType = null; + final BigDecimal depositAmount = null; + final LocalDate maturityDate = null; + final Integer depositPeriod = null; + final EnumOptionData depositPeriodFrequency = null; + final BigDecimal recurringDepositAmount = null; + final EnumOptionData onAccountClosure = null; + + final Collection interestFreePeriodTypeOptions = null; + final Collection preClosurePenalInterestOnTypeOptions = null; + final Collection depositTermTypeOptions = null; + final Collection inMultiplesOfDepositTermTypeOptions = null; + + final EnumOptionData recurringDepositType = null; + final Integer recurringDepositFrequency = null; + final EnumOptionData recurringDepositFrequencyType = null; + final Collection recurringDepositTypeOptions = null; + final Collection recurringDepositFrequencyTypeOptions = null; + final Collection depositPeriodFrequencyOptions = null; + final EnumOptionData depositType = SavingsEnumerations.depositType(DepositAccountType.RECURRING_DEPOSIT.getValue()); + + final Long groupId = null; + final String groupName = null; + final Long clientId = null; + final String clientName = null; + + return new RecurringDepositAccountData(accountId, accountNo, externalId, groupId, groupName, clientId, clientName, productId, + productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType, + interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions, + fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, + interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, + withdrawalFeeTypeOptions, charges, chargeOptions, accountChart, chartTemplate, interestFreePeriodApplicable, + interestFreeFromPeriod, interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, + preClosurePenalInterest, preClosurePenalInterestOnType, interestFreePeriodTypeOptions, + preClosurePenalInterestOnTypeOptions, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, + inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod, + depositPeriodFrequency, recurringDepositAmount, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions, + recurringDepositType, recurringDepositFrequency, recurringDepositFrequencyType, recurringDepositTypeOptions, + recurringDepositFrequencyTypeOptions, depositPeriodFrequencyOptions, depositType, onAccountClosure, + onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas); + } + + public static RecurringDepositAccountData withClosureTemplateDetails(final RecurringDepositAccountData account, + final Collection onAccountClosureOptions, final Collection paymentTypeOptions, final Collection savingsAccountDatas) { + + return new RecurringDepositAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName, + account.clientId, account.clientName, account.savingsProductId, account.savingsProductName, account.fieldOfficerId, + account.fieldOfficerName, account.status, account.timeline, account.currency, account.nominalAnnualInterestRate, + account.interestCompoundingPeriodType, account.interestPostingPeriodType, account.interestCalculationType, + account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency, + account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers, account.summary, account.transactions, + account.productOptions, account.fieldOfficerOptions, account.interestCompoundingPeriodTypeOptions, + account.interestPostingPeriodTypeOptions, account.interestCalculationTypeOptions, + account.interestCalculationDaysInYearTypeOptions, account.lockinPeriodFrequencyTypeOptions, + account.withdrawalFeeTypeOptions, account.charges, account.chargeOptions, account.accountChart, account.chartTemplate, + account.interestFreePeriodApplicable, account.interestFreeFromPeriod, account.interestFreeToPeriod, + account.interestFreePeriodFrequencyType, account.preClosurePenalApplicable, account.preClosurePenalInterest, + account.preClosurePenalInterestOnType, account.interestFreePeriodTypeOptions, account.preClosurePenalInterestOnTypeOptions, + account.minDepositTerm, account.maxDepositTerm, account.minDepositTermType, account.maxDepositTermType, + account.inMultiplesOfDepositTerm, account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, + account.maturityDate, account.depositPeriod, account.depositPeriodFrequency, account.recurringDepositAmount, + account.depositTermTypeOptions, account.inMultiplesOfDepositTermTypeOptions, account.recurringDepositType, + account.recurringDepositFrequency, account.recurringDepositFrequencyType, account.recurringDepositTypeOptions, + account.recurringDepositFrequencyTypeOptions, account.depositPeriodFrequencyOptions, account.depositType, + account.onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas); + } + + private RecurringDepositAccountData(final Long id, final String accountNo, final String externalId, final Long groupId, + final String groupName, final Long clientId, final String clientName, final Long productId, final String productName, + final Long fieldofficerId, final String fieldofficerName, final SavingsAccountStatusEnumData status, + final SavingsAccountApplicationTimelineData timeline, final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, + final EnumOptionData interestPeriodType, final EnumOptionData interestPostingPeriodType, + final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType, + final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, + final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers, + final SavingsAccountSummaryData summary, final Collection transactions, + final Collection productOptions, final Collection fieldOfficerOptions, + final Collection interestCompoundingPeriodTypeOptions, + final Collection interestPostingPeriodTypeOptions, + final Collection interestCalculationTypeOptions, + final Collection interestCalculationDaysInYearTypeOptions, + final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, + final Collection charges, final Collection chargeOptions, + final DepositAccountInterestRateChartData accountChart, final DepositAccountInterestRateChartData chartTemplate, + final boolean interestFreePeriodApplicable, final Integer interestFreeFromPeriod, final Integer interestFreeToPeriod, + final EnumOptionData interestFreePeriodFrequencyType, final boolean preClosurePenalApplicable, + final BigDecimal preClosurePenalInterest, final EnumOptionData preClosurePenalInterestOnType, + final Collection interestFreePeriodTypeOptions, + final Collection preClosurePenalInterestOnTypeOptions, final Integer minDepositTerm, + final Integer maxDepositTerm, final EnumOptionData minDepositTermType, final EnumOptionData maxDepositTermType, + final Integer inMultiplesOfDepositTerm, final EnumOptionData inMultiplesOfDepositTermType, final BigDecimal depositAmount, + final BigDecimal maturityAmount, final LocalDate maturityDate, final Integer depositPeriod, + final EnumOptionData depositPeriodFrequency, final BigDecimal recurringDepositAmount, + final Collection depositTermTypeOptions, Collection inMultiplesOfDepositTermTypeOptions, + final EnumOptionData recurringDepositType, final Integer recurringDepositFrequency, + final EnumOptionData recurringDepositFrequencyType, final Collection recurringDepositTypeOptions, + final Collection recurringDepositFrequencyTypeOptions, + final Collection depositPeriodFrequencyOptions, final EnumOptionData depositType, + final EnumOptionData onAccountClosure, final Collection onAccountClosureOptions, + final Collection paymentTypeOptions, final Collection savingsAccountDatas) { + + super(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, fieldofficerId, + fieldofficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType, interestPostingPeriodType, + interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, + lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions, fieldOfficerOptions, + interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, + chargeOptions, accountChart, chartTemplate, depositType); + + this.interestFreePeriodApplicable = interestFreePeriodApplicable; + this.interestFreeFromPeriod = interestFreeFromPeriod; + this.interestFreeToPeriod = interestFreeToPeriod; + this.interestFreePeriodFrequencyType = interestFreePeriodFrequencyType; + this.preClosurePenalApplicable = preClosurePenalApplicable; + this.preClosurePenalInterest = preClosurePenalInterest; + this.preClosurePenalInterestOnType = preClosurePenalInterestOnType; + this.minDepositTerm = minDepositTerm; + this.maxDepositTerm = maxDepositTerm; + this.minDepositTermType = minDepositTermType; + this.maxDepositTermType = maxDepositTermType; + this.inMultiplesOfDepositTerm = inMultiplesOfDepositTerm; + this.inMultiplesOfDepositTermType = inMultiplesOfDepositTermType; + this.depositAmount = depositAmount; + this.maturityAmount = maturityAmount; + this.maturityDate = maturityDate; + this.depositPeriod = depositPeriod; + this.depositPeriodFrequency = depositPeriodFrequency; + this.recurringDepositAmount = recurringDepositAmount; + this.recurringDepositFrequencyType = recurringDepositFrequencyType; + this.recurringDepositFrequency = recurringDepositFrequency; + this.recurringDepositType = recurringDepositType; + + // template data + this.interestFreePeriodTypeOptions = interestFreePeriodTypeOptions; + this.preClosurePenalInterestOnTypeOptions = preClosurePenalInterestOnTypeOptions; + this.recurringDepositTypeOptions = recurringDepositTypeOptions; + this.recurringDepositFrequencyTypeOptions = recurringDepositFrequencyTypeOptions; + this.depositTermTypeOptions = depositTermTypeOptions; + this.inMultiplesOfDepositTermTypeOptions = inMultiplesOfDepositTermTypeOptions; + this.depositPeriodFrequencyOptions = depositPeriodFrequencyOptions; + this.onAccountClosure = onAccountClosure; + this.savingsAccounts = savingsAccountDatas; + + // account close template options + this.onAccountClosureOptions = onAccountClosureOptions; + this.paymentTypeOptions = paymentTypeOptions; + } + + @Override + public boolean equals(final Object obj) { + + if (obj == null) { return false; } + if (obj == this) { return true; } + if (obj.getClass() != getClass()) { return false; } + final RecurringDepositAccountData rhs = (RecurringDepositAccountData) obj; + return new EqualsBuilder().append(this.id, rhs.id).append(this.accountNo, rhs.accountNo).isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37).append(this.id).append(this.accountNo).toHashCode(); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/RecurringDepositProductData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/RecurringDepositProductData.java new file mode 100644 index 000000000..0309a65a7 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/RecurringDepositProductData.java @@ -0,0 +1,433 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.data; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.mifosplatform.accounting.glaccount.data.GLAccountData; +import org.mifosplatform.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper; +import org.mifosplatform.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper; +import org.mifosplatform.infrastructure.codes.data.CodeValueData; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.organisation.monetary.data.CurrencyData; +import org.mifosplatform.portfolio.charge.data.ChargeData; +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartData; + +/** + * Immutable data object represent a Recurring Deposit product. + */ +public class RecurringDepositProductData extends DepositProductData { + + private boolean interestFreePeriodApplicable; + private Integer interestFreeFromPeriod; + private Integer interestFreeToPeriod; + protected EnumOptionData interestFreePeriodFrequencyType; + private boolean preClosurePenalApplicable; + private BigDecimal preClosurePenalInterest; + private EnumOptionData preClosurePenalInterestOnType; + private Integer minDepositTerm; + private Integer maxDepositTerm; + private EnumOptionData minDepositTermType; + private EnumOptionData maxDepositTermType; + private Integer inMultiplesOfDepositTerm; + private EnumOptionData inMultiplesOfDepositTermType; + private EnumOptionData recurringDepositType; + private Integer recurringDepositFrequency; + private EnumOptionData recurringDepositFrequencyType; + + private Collection interestFreePeriodTypeOptions; + private Collection preClosurePenalInterestOnTypeOptions; + private Collection recurringDepositTypeOptions; + private Collection recurringDepositFrequencyTypeOptions; + private Collection depositTermTypeOptions; + private Collection inMultiplesOfDepositTermTypeOptions; + + public static RecurringDepositProductData template(final CurrencyData currency, final EnumOptionData interestCompoundingPeriodType, + final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType, + final EnumOptionData interestCalculationDaysInYearType, final EnumOptionData accountingRule, + final Collection currencyOptions, final Collection interestCompoundingPeriodTypeOptions, + final Collection interestPostingPeriodTypeOptions, + final Collection interestCalculationTypeOptions, + final Collection interestCalculationDaysInYearTypeOptions, + final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, + final Collection paymentTypeOptions, final Collection accountingRuleOptions, + final Map> accountingMappingOptions, final Collection chargeOptions, + final Collection penaltyOptions, final InterestRateChartData chartTemplate, + final Collection interestFreePeriodTypes, + final Collection preClosurePenalInterestOnTypeOptions, + final Collection recurringDepositTypeOptions, + final Collection recurringDepositFrequencyTypeOptions, final Collection depositTermTypeOptions, + final Collection inMultiplesOfDepositTermTypeOptions) { + + final Long id = null; + final String name = null; + final String shortName = null; + final String description = null; + final BigDecimal nominalAnnualInterestRate = null; + + final Integer lockinPeriodFrequency = null; + final EnumOptionData lockinPeriodFrequencyType = null; + + final Map accountingMappings = null; + final Collection paymentChannelToFundSourceMappings = null; + final Collection charges = null; + final Collection feeToIncomeAccountMappings = null; + final Collection penaltyToIncomeAccountMappings = null; + final Collection interestRateCharts = null; + final boolean interestFreePeriodApplicable = false; + final Integer interestFreeFromPeriod = null; + final Integer interestFreeToPeriod = null; + final EnumOptionData interestFreePeriodFrequencyType = null; + final boolean preClosurePenalApplicable = false; + final BigDecimal preClosurePenalInterest = null; + final EnumOptionData preClosurePenalInterestOnType = null; + final EnumOptionData recurringDepositType = null; + final Integer recurringDepositFrequency = null; + final EnumOptionData recurringDepositFrequencyType = null; + final Integer minDepositTerm = null; + final Integer maxDepositTerm = null; + final EnumOptionData minDepositTermType = null; + final EnumOptionData maxDepositTermType = null; + final Integer inMultiplesOfDepositTerm = null; + final EnumOptionData inMultiplesOfDepositTermType = null; + + return new RecurringDepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, + interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, + lockinPeriodFrequency, lockinPeriodFrequencyType, accountingRule, accountingMappings, paymentChannelToFundSourceMappings, + currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, + accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, + penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, interestFreePeriodApplicable, interestFreeFromPeriod, + interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest, + preClosurePenalInterestOnType, interestFreePeriodTypes, preClosurePenalInterestOnTypeOptions, minDepositTerm, + maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, + recurringDepositType, recurringDepositFrequency, recurringDepositFrequencyType, recurringDepositTypeOptions, + recurringDepositFrequencyTypeOptions, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions); + } + + public static RecurringDepositProductData withCharges(final RecurringDepositProductData existingProduct, + final Collection charges) { + return new RecurringDepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, + existingProduct.description, existingProduct.currency, existingProduct.nominalAnnualInterestRate, + existingProduct.interestCompoundingPeriodType, existingProduct.interestPostingPeriodType, + existingProduct.interestCalculationType, existingProduct.interestCalculationDaysInYearType, + existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType, existingProduct.accountingRule, + existingProduct.accountingMappings, existingProduct.paymentChannelToFundSourceMappings, existingProduct.currencyOptions, + existingProduct.interestCompoundingPeriodTypeOptions, existingProduct.interestPostingPeriodTypeOptions, + existingProduct.interestCalculationTypeOptions, existingProduct.interestCalculationDaysInYearTypeOptions, + existingProduct.lockinPeriodFrequencyTypeOptions, existingProduct.withdrawalFeeTypeOptions, + existingProduct.paymentTypeOptions, existingProduct.accountingRuleOptions, existingProduct.accountingMappingOptions, + charges, existingProduct.chargeOptions, existingProduct.penaltyOptions, existingProduct.feeToIncomeAccountMappings, + existingProduct.penaltyToIncomeAccountMappings, existingProduct.interestRateCharts, existingProduct.chartTemplate, + existingProduct.interestFreePeriodApplicable, existingProduct.interestFreeFromPeriod, existingProduct.interestFreeToPeriod, + existingProduct.interestFreePeriodFrequencyType, existingProduct.preClosurePenalApplicable, + existingProduct.preClosurePenalInterest, existingProduct.preClosurePenalInterestOnType, + existingProduct.interestFreePeriodTypeOptions, existingProduct.preClosurePenalInterestOnTypeOptions, + existingProduct.minDepositTerm, existingProduct.maxDepositTerm, existingProduct.minDepositTermType, + existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm, existingProduct.inMultiplesOfDepositTermType, + existingProduct.recurringDepositType, existingProduct.recurringDepositFrequency, + existingProduct.recurringDepositFrequencyType, existingProduct.recurringDepositTypeOptions, + existingProduct.recurringDepositFrequencyTypeOptions, existingProduct.depositTermTypeOptions, + existingProduct.inMultiplesOfDepositTermTypeOptions); + } + + /** + * Returns a {@link RecurringDepositProductData} that contains and exist + * {@link RecurringDepositProductData} data with further template data for + * dropdowns. + */ + public static RecurringDepositProductData withTemplate(final RecurringDepositProductData existingProduct, + final Collection currencyOptions, final Collection interestCompoundingPeriodTypeOptions, + final Collection interestPostingPeriodTypeOptions, + final Collection interestCalculationTypeOptions, + final Collection interestCalculationDaysInYearTypeOptions, + final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, + final Collection paymentTypeOptions, final Collection accountingRuleOptions, + final Map> accountingMappingOptions, final Collection chargeOptions, + final Collection penaltyOptions, final InterestRateChartData chartTemplate, + final Collection interestFreePeriodTypes, + final Collection preClosurePenalInterestOnTypeOptions, + final Collection recurringDepositTypeOptions, + final Collection recurringDepositFrequencyTypeOptions, final Collection depositTermTypeOptions, + final Collection inMultiplesOfDepositTermTypeOptions) { + + return new RecurringDepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, + existingProduct.description, existingProduct.currency, existingProduct.nominalAnnualInterestRate, + existingProduct.interestCompoundingPeriodType, existingProduct.interestPostingPeriodType, + existingProduct.interestCalculationType, existingProduct.interestCalculationDaysInYearType, + existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType, existingProduct.accountingRule, + existingProduct.accountingMappings, existingProduct.paymentChannelToFundSourceMappings, currencyOptions, + interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, + accountingRuleOptions, accountingMappingOptions, existingProduct.charges, chargeOptions, penaltyOptions, + existingProduct.feeToIncomeAccountMappings, existingProduct.penaltyToIncomeAccountMappings, + existingProduct.interestRateCharts, chartTemplate, existingProduct.interestFreePeriodApplicable, + existingProduct.interestFreeFromPeriod, existingProduct.interestFreeToPeriod, + existingProduct.interestFreePeriodFrequencyType, existingProduct.preClosurePenalApplicable, + existingProduct.preClosurePenalInterest, existingProduct.preClosurePenalInterestOnType, interestFreePeriodTypes, + preClosurePenalInterestOnTypeOptions, existingProduct.minDepositTerm, existingProduct.maxDepositTerm, + existingProduct.minDepositTermType, existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm, + existingProduct.inMultiplesOfDepositTermType, existingProduct.recurringDepositType, + existingProduct.recurringDepositFrequency, existingProduct.recurringDepositFrequencyType, recurringDepositTypeOptions, + recurringDepositFrequencyTypeOptions, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions); + } + + public static RecurringDepositProductData withAccountingDetails(final RecurringDepositProductData existingProduct, + final Map accountingMappings, + final Collection paymentChannelToFundSourceMappings, + final Collection feeToIncomeAccountMappings, + final Collection penaltyToIncomeAccountMappings) { + + final Collection currencyOptions = null; + final Collection interestCompoundingPeriodTypeOptions = null; + final Collection interestPostingPeriodTypeOptions = null; + final Collection interestCalculationTypeOptions = null; + final Collection interestCalculationDaysInYearTypeOptions = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + final Collection paymentTypeOptions = null; + final Collection accountingRuleOptions = null; + final Map> accountingMappingOptions = null; + final Collection chargeOptions = null; + final Collection penaltyOptions = null; + + return new RecurringDepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, + existingProduct.description, existingProduct.currency, existingProduct.nominalAnnualInterestRate, + existingProduct.interestCompoundingPeriodType, existingProduct.interestPostingPeriodType, + existingProduct.interestCalculationType, existingProduct.interestCalculationDaysInYearType, + existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType, existingProduct.accountingRule, + accountingMappings, paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions, + interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, + lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, + accountingMappingOptions, existingProduct.charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, + penaltyToIncomeAccountMappings, existingProduct.interestRateCharts, existingProduct.chartTemplate, + existingProduct.interestFreePeriodApplicable, existingProduct.interestFreeFromPeriod, existingProduct.interestFreeToPeriod, + existingProduct.interestFreePeriodFrequencyType, existingProduct.preClosurePenalApplicable, + existingProduct.preClosurePenalInterest, existingProduct.preClosurePenalInterestOnType, + existingProduct.interestFreePeriodTypeOptions, existingProduct.preClosurePenalInterestOnTypeOptions, + existingProduct.minDepositTerm, existingProduct.maxDepositTerm, existingProduct.minDepositTermType, + existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm, existingProduct.inMultiplesOfDepositTermType, + existingProduct.recurringDepositType, existingProduct.recurringDepositFrequency, + existingProduct.recurringDepositFrequencyType, existingProduct.recurringDepositTypeOptions, + existingProduct.recurringDepositFrequencyTypeOptions, existingProduct.depositTermTypeOptions, + existingProduct.inMultiplesOfDepositTermTypeOptions); + } + + public static RecurringDepositProductData instance(final DepositProductData depositProductData, + final boolean interestFreePeriodApplicable, final Integer interestFreeFromPeriod, final Integer interestFreeToPeriod, + final EnumOptionData interestFreePeriodFrequencyType, final boolean preClosurePenalApplicable, + final BigDecimal preClosurePenalInterest, final EnumOptionData preClosurePenalInterestOnType, final Integer minDepositTerm, + final Integer maxDepositTerm, final EnumOptionData minDepositTermType, final EnumOptionData maxDepositTermType, + final Integer inMultiplesOfDepositTerm, final EnumOptionData inMultiplesOfDepositTermType, + final EnumOptionData recurringDepositType, final Integer recurringDepositFrequency, + final EnumOptionData recurringDepositFrequencyType) { + + final Map accountingMappings = null; + final Collection paymentChannelToFundSourceMappings = null; + + final Collection currencyOptions = null; + final Collection interestCompoundingPeriodTypeOptions = null; + final Collection interestPostingPeriodTypeOptions = null; + final Collection interestCalculationTypeOptions = null; + final Collection interestCalculationDaysInYearTypeOptions = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + final Collection paymentTypeOptions = null; + final Collection accountingRuleOptions = null; + final Map> accountingMappingOptions = null; + final Collection chargeOptions = null; + final Collection penaltyOptions = null; + final Collection charges = null; + final Collection feeToIncomeAccountMappings = null; + final Collection penaltyToIncomeAccountMappings = null; + final Collection interestRateCharts = null; + final InterestRateChartData chartTemplate = null; + final Collection interestFreePeriodTypes = null; + final Collection preClosurePenalInterestOnTypeOptions = null; + final Collection recurringDepositTypeOptions = null; + final Collection recurringDepositFrequencyTypeOptions = null; + final Collection depositTermTypeOptions = null; + final Collection inMultiplesOfDepositTermTypeOptions = null; + + return new RecurringDepositProductData(depositProductData.id, depositProductData.name, depositProductData.shortName, + depositProductData.description, depositProductData.currency, depositProductData.nominalAnnualInterestRate, + depositProductData.interestCompoundingPeriodType, depositProductData.interestPostingPeriodType, + depositProductData.interestCalculationType, depositProductData.interestCalculationDaysInYearType, + depositProductData.lockinPeriodFrequency, depositProductData.lockinPeriodFrequencyType, depositProductData.accountingRule, + accountingMappings, paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions, + interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, + lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, + accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, + penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, interestFreePeriodApplicable, interestFreeFromPeriod, + interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest, + preClosurePenalInterestOnType, interestFreePeriodTypes, preClosurePenalInterestOnTypeOptions, minDepositTerm, + maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, + recurringDepositType, recurringDepositFrequency, recurringDepositFrequencyType, recurringDepositTypeOptions, + recurringDepositFrequencyTypeOptions, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions); + } + + public static RecurringDepositProductData lookup(final Long id, final String name) { + + final String shortName = null; + final CurrencyData currency = null; + final String description = null; + final BigDecimal nominalAnnualInterestRate = null; + final EnumOptionData interestCompoundingPeriodType = null; + final EnumOptionData interestPostingPeriodType = null; + final EnumOptionData interestCalculationType = null; + final EnumOptionData interestCalculationDaysInYearType = null; + + final Integer lockinPeriodFrequency = null; + final EnumOptionData lockinPeriodFrequencyType = null; + + final EnumOptionData accountingType = null; + final Map accountingMappings = null; + final Collection paymentChannelToFundSourceMappings = null; + + final Collection currencyOptions = null; + final Collection interestCompoundingPeriodTypeOptions = null; + final Collection interestPostingPeriodTypeOptions = null; + final Collection interestCalculationTypeOptions = null; + final Collection interestCalculationDaysInYearTypeOptions = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + final Collection paymentTypeOptions = null; + final Collection accountingRuleOptions = null; + final Map> accountingMappingOptions = null; + final Collection charges = null; + final Collection chargeOptions = null; + final Collection penaltyOptions = null; + final Collection feeToIncomeAccountMappings = null; + final Collection penaltyToIncomeAccountMappings = null; + final Collection interestRateCharts = null; + final InterestRateChartData chartTemplate = null; + final boolean interestFreePeriodApplicable = false; + final Integer interestFreeFromPeriod = null; + final Integer interestFreeToPeriod = null; + final EnumOptionData interestFreePeriodFrequencyType = null; + final boolean preClosurePenalApplicable = false; + final BigDecimal preClosurePenalInterest = null; + final EnumOptionData preClosurePenalInterestOnType = null; + final Collection interestFreePeriodTypes = null; + final Collection preClosurePenalInterestOnTypeOptions = null; + final EnumOptionData recurringDepositType = null; + final Integer recurringDepositFrequency = null; + final EnumOptionData recurringDepositFrequencyType = null; + final Collection recurringDepositTypeOptions = null; + final Collection recurringDepositFrequencyTypeOptions = null; + final Integer minDepositTerm = null; + final Integer maxDepositTerm = null; + final EnumOptionData minDepositTermType = null; + final EnumOptionData maxDepositTermType = null; + final Integer inMultiplesOfDepositTerm = null; + final EnumOptionData inMultiplesOfDepositTermType = null; + final Collection depositTermTypeOptions = null; + final Collection inMultiplesOfDepositTermTypeOptions = null; + + return new RecurringDepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, + interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, + lockinPeriodFrequency, lockinPeriodFrequencyType, accountingType, accountingMappings, paymentChannelToFundSourceMappings, + currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, + accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, + penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, interestFreePeriodApplicable, interestFreeFromPeriod, + interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest, + preClosurePenalInterestOnType, interestFreePeriodTypes, preClosurePenalInterestOnTypeOptions, minDepositTerm, + maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, + recurringDepositType, recurringDepositFrequency, recurringDepositFrequencyType, recurringDepositTypeOptions, + recurringDepositFrequencyTypeOptions, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions); + } + + public static RecurringDepositProductData withInterestChart(final RecurringDepositProductData product, + final Collection interestRateCharts) { + return new RecurringDepositProductData(product.id, product.name, product.shortName, product.description, product.currency, + product.nominalAnnualInterestRate, product.interestCompoundingPeriodType, product.interestPostingPeriodType, + product.interestCalculationType, product.interestCalculationDaysInYearType, product.lockinPeriodFrequency, + product.lockinPeriodFrequencyType, product.accountingRule, product.accountingMappings, + product.paymentChannelToFundSourceMappings, product.currencyOptions, product.interestCompoundingPeriodTypeOptions, + product.interestPostingPeriodTypeOptions, product.interestCalculationTypeOptions, + product.interestCalculationDaysInYearTypeOptions, product.lockinPeriodFrequencyTypeOptions, + product.withdrawalFeeTypeOptions, product.paymentTypeOptions, product.accountingRuleOptions, + product.accountingMappingOptions, product.charges, product.chargeOptions, product.penaltyOptions, + product.feeToIncomeAccountMappings, product.penaltyToIncomeAccountMappings, interestRateCharts, product.chartTemplate, + product.interestFreePeriodApplicable, product.interestFreeFromPeriod, product.interestFreeToPeriod, + product.interestFreePeriodFrequencyType, product.preClosurePenalApplicable, product.preClosurePenalInterest, + product.preClosurePenalInterestOnType, product.interestFreePeriodTypeOptions, product.preClosurePenalInterestOnTypeOptions, + product.minDepositTerm, product.maxDepositTerm, product.minDepositTermType, product.maxDepositTermType, + product.inMultiplesOfDepositTerm, product.inMultiplesOfDepositTermType, product.recurringDepositType, + product.recurringDepositFrequency, product.recurringDepositFrequencyType, product.recurringDepositTypeOptions, + product.recurringDepositFrequencyTypeOptions, product.depositTermTypeOptions, product.inMultiplesOfDepositTermTypeOptions); + } + + private RecurringDepositProductData(final Long id, final String name, final String shortName, final String description, + final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, final EnumOptionData interestCompoundingPeriodType, + final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType, + final EnumOptionData interestCalculationDaysInYearType, final Integer lockinPeriodFrequency, + final EnumOptionData lockinPeriodFrequencyType, final EnumOptionData accountingType, + final Map accountingMappings, + final Collection paymentChannelToFundSourceMappings, + final Collection currencyOptions, final Collection interestCompoundingPeriodTypeOptions, + final Collection interestPostingPeriodTypeOptions, + final Collection interestCalculationTypeOptions, + final Collection interestCalculationDaysInYearTypeOptions, + final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, + final Collection paymentTypeOptions, final Collection accountingRuleOptions, + final Map> accountingMappingOptions, final Collection charges, + final Collection chargeOptions, final Collection penaltyOptions, + final Collection feeToIncomeAccountMappings, + final Collection penaltyToIncomeAccountMappings, + final Collection interestRateCharts, final InterestRateChartData chartTemplate, + final boolean interestFreePeriodApplicable, final Integer interestFreeFromPeriod, final Integer interestFreeToPeriod, + final EnumOptionData interestFreePeriodFrequencyType, final boolean preClosurePenalApplicable, + final BigDecimal preClosurePenalInterest, final EnumOptionData preClosurePenalInterestOnType, + final Collection interestFreePeriodTypeOptions, + final Collection preClosurePenalInterestOnTypeOptions, final Integer minDepositTerm, + final Integer maxDepositTerm, final EnumOptionData minDepositTermType, final EnumOptionData maxDepositTermType, + final Integer inMultiplesOfDepositTerm, final EnumOptionData inMultiplesOfDepositTermType, + final EnumOptionData recurringDepositType, final Integer recurringDepositFrequency, + final EnumOptionData recurringDepositFrequencyType, final Collection recurringDepositTypeOptions, + final Collection recurringDepositFrequencyTypeOptions, final Collection depositTermTypeOptions, + final Collection inMultiplesOfDepositTermTypeOptions) { + + super(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, + interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, lockinPeriodFrequency, + lockinPeriodFrequencyType, accountingType, accountingMappings, paymentChannelToFundSourceMappings, currencyOptions, + interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, + accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, + penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate); + + this.interestFreePeriodApplicable = interestFreePeriodApplicable; + this.interestFreeFromPeriod = interestFreeFromPeriod; + this.interestFreeToPeriod = interestFreeToPeriod; + this.interestFreePeriodFrequencyType = interestFreePeriodFrequencyType; + this.preClosurePenalApplicable = preClosurePenalApplicable; + this.preClosurePenalInterest = preClosurePenalInterest; + this.preClosurePenalInterestOnType = preClosurePenalInterestOnType; + this.minDepositTerm = minDepositTerm; + this.maxDepositTerm = maxDepositTerm; + this.minDepositTermType = minDepositTermType; + this.maxDepositTermType = maxDepositTermType; + this.inMultiplesOfDepositTerm = inMultiplesOfDepositTerm; + this.inMultiplesOfDepositTermType = inMultiplesOfDepositTermType; + this.recurringDepositFrequencyType = recurringDepositFrequencyType; + this.recurringDepositFrequency = recurringDepositFrequency; + this.recurringDepositType = recurringDepositType; + + // template data + this.interestFreePeriodTypeOptions = interestFreePeriodTypeOptions; + this.preClosurePenalInterestOnTypeOptions = preClosurePenalInterestOnTypeOptions; + this.recurringDepositTypeOptions = recurringDepositTypeOptions; + this.recurringDepositFrequencyTypeOptions = recurringDepositFrequencyTypeOptions; + this.depositTermTypeOptions = depositTermTypeOptions; + this.inMultiplesOfDepositTermTypeOptions = inMultiplesOfDepositTermTypeOptions; + + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountStatusEnumData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountStatusEnumData.java index 904958226..ad9ed073b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountStatusEnumData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountStatusEnumData.java @@ -28,13 +28,15 @@ public class SavingsAccountStatusEnumData { @SuppressWarnings("unused") private final boolean closed; @SuppressWarnings("unused") + private final boolean prematureClosed; + @SuppressWarnings("unused") private final boolean transferInProgress; @SuppressWarnings("unused") private final boolean transferOnHold; public SavingsAccountStatusEnumData(final Long id, final String code, final String value, final boolean submittedAndPendingApproval, final boolean approved, final boolean rejected, final boolean withdrawnByApplicant, final boolean active, final boolean closed, - final boolean transferInProgress, final boolean transferOnHold) { + final boolean prematureClosed, final boolean transferInProgress, final boolean transferOnHold) { this.id = id; this.code = code; this.value = value; @@ -44,6 +46,7 @@ public class SavingsAccountStatusEnumData { this.withdrawnByApplicant = withdrawnByApplicant; this.active = active; this.closed = closed; + this.prematureClosed = prematureClosed; this.transferInProgress = transferInProgress; this.transferOnHold = transferOnHold; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountAssembler.java new file mode 100644 index 000000000..287d0a529 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountAssembler.java @@ -0,0 +1,368 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.chartIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositAmountParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositPeriodFrequencyIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositPeriodParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositAmountParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositFrequencyParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositFrequencyTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositTypeIdParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountNoParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.clientIdParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.externalIdParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.fieldOfficerIdParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.groupIdParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.productIdParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.submittedOnDateParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; + +import java.math.BigDecimal; +import java.util.Set; + +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.infrastructure.core.service.DateUtils; +import org.mifosplatform.organisation.staff.domain.Staff; +import org.mifosplatform.organisation.staff.domain.StaffRepositoryWrapper; +import org.mifosplatform.portfolio.accountdetails.domain.AccountType; +import org.mifosplatform.portfolio.client.domain.Client; +import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; +import org.mifosplatform.portfolio.client.exception.ClientNotActiveException; +import org.mifosplatform.portfolio.group.domain.Group; +import org.mifosplatform.portfolio.group.domain.GroupRepositoryWrapper; +import org.mifosplatform.portfolio.group.exception.CenterNotActiveException; +import org.mifosplatform.portfolio.group.exception.ClientNotInGroupException; +import org.mifosplatform.portfolio.group.exception.GroupNotActiveException; +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChart; +import org.mifosplatform.portfolio.savings.DepositAccountOnClosureType; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.RecurringDepositType; +import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; +import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; +import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType; +import org.mifosplatform.portfolio.savings.exception.FixedDepositProductNotFoundException; +import org.mifosplatform.portfolio.savings.exception.RecurringDepositProductNotFoundException; +import org.mifosplatform.portfolio.savings.exception.SavingsProductNotFoundException; +import org.mifosplatform.useradministration.domain.AppUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.google.gson.JsonElement; + +@Service +public class DepositAccountAssembler { + + private final SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper; + private final SavingsHelper savingsHelper = new SavingsHelper(); + private final ClientRepositoryWrapper clientRepository; + private final GroupRepositoryWrapper groupRepository; + private final StaffRepositoryWrapper staffRepository; + private final FixedDepositProductRepository fixedDepositProductRepository; + private final RecurringDepositProductRepository recurringDepositProductRepository; + private final SavingsAccountRepositoryWrapper savingsAccountRepository; + private final SavingsAccountChargeAssembler savingsAccountChargeAssembler; + private final FromJsonHelper fromApiJsonHelper; + private final DepositProductAssembler depositProductAssembler; + + @Autowired + public DepositAccountAssembler(final SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper, + final ClientRepositoryWrapper clientRepository, final GroupRepositoryWrapper groupRepository, + final StaffRepositoryWrapper staffRepository, final FixedDepositProductRepository fixedDepositProductRepository, + final SavingsAccountRepositoryWrapper savingsAccountRepository, + final SavingsAccountChargeAssembler savingsAccountChargeAssembler, final FromJsonHelper fromApiJsonHelper, + final DepositProductAssembler depositProductAssembler, final RecurringDepositProductRepository recurringDepositProductRepository) { + this.savingsAccountTransactionSummaryWrapper = savingsAccountTransactionSummaryWrapper; + this.clientRepository = clientRepository; + this.groupRepository = groupRepository; + this.staffRepository = staffRepository; + this.fixedDepositProductRepository = fixedDepositProductRepository; + this.savingsAccountRepository = savingsAccountRepository; + this.savingsAccountChargeAssembler = savingsAccountChargeAssembler; + this.fromApiJsonHelper = fromApiJsonHelper; + this.depositProductAssembler = depositProductAssembler; + this.recurringDepositProductRepository = recurringDepositProductRepository; + } + + /** + * Assembles a new {@link SavingsAccount} from JSON details passed in + * request inheriting details where relevant from chosen + * {@link SavingsProduct}. + */ + public SavingsAccount assembleFrom(final JsonCommand command, final AppUser submittedBy, final DepositAccountType depositAccountType) { + + final JsonElement element = command.parsedJson(); + + final String accountNo = this.fromApiJsonHelper.extractStringNamed(accountNoParamName, element); + final String externalId = this.fromApiJsonHelper.extractStringNamed(externalIdParamName, element); + final Long productId = this.fromApiJsonHelper.extractLongNamed(productIdParamName, element); + + SavingsProduct product = null; + if (depositAccountType.isFixedDeposit()) { + product = this.fixedDepositProductRepository.findOne(productId); + if (product == null) { throw new FixedDepositProductNotFoundException(productId); } + } else if (depositAccountType.isRecurringDeposit()) { + product = this.recurringDepositProductRepository.findOne(productId); + if (product == null) { throw new RecurringDepositProductNotFoundException(productId); } + } + + if (product == null) { throw new SavingsProductNotFoundException(productId); } + + Client client = null; + Group group = null; + Staff fieldOfficer = null; + AccountType accountType = AccountType.INVALID; + final Long clientId = this.fromApiJsonHelper.extractLongNamed(clientIdParamName, element); + if (clientId != null) { + client = this.clientRepository.findOneWithNotFoundDetection(clientId); + accountType = AccountType.INDIVIDUAL; + if (client.isNotActive()) { throw new ClientNotActiveException(clientId); } + } + + final Long groupId = this.fromApiJsonHelper.extractLongNamed(groupIdParamName, element); + if (groupId != null) { + group = this.groupRepository.findOneWithNotFoundDetection(groupId); + accountType = AccountType.GROUP; + } + + if (group != null && client != null) { + if (!group.hasClientAsMember(client)) { throw new ClientNotInGroupException(clientId, groupId); } + accountType = AccountType.JLG; + if (group.isNotActive()) { + if (group.isCenter()) { throw new CenterNotActiveException(groupId); } + throw new GroupNotActiveException(groupId); + } + } + + final Long fieldOfficerId = this.fromApiJsonHelper.extractLongNamed(fieldOfficerIdParamName, element); + if (fieldOfficerId != null) { + fieldOfficer = this.staffRepository.findOneWithNotFoundDetection(fieldOfficerId); + } + + final LocalDate submittedOnDate = this.fromApiJsonHelper.extractLocalDateNamed(submittedOnDateParamName, element); + + BigDecimal interestRate = null; + if (command.parameterExists(nominalAnnualInterestRateParamName)) { + interestRate = command.bigDecimalValueOfParameterNamed(nominalAnnualInterestRateParamName); + } else { + interestRate = product.nominalAnnualInterestRate(); + } + + SavingsCompoundingInterestPeriodType interestCompoundingPeriodType = null; + final Integer interestPeriodTypeValue = command.integerValueOfParameterNamed(interestCompoundingPeriodTypeParamName); + if (interestPeriodTypeValue != null) { + interestCompoundingPeriodType = SavingsCompoundingInterestPeriodType.fromInt(interestPeriodTypeValue); + } else { + interestCompoundingPeriodType = product.interestCompoundingPeriodType(); + } + + SavingsPostingInterestPeriodType interestPostingPeriodType = null; + final Integer interestPostingPeriodTypeValue = command.integerValueOfParameterNamed(interestPostingPeriodTypeParamName); + if (interestPostingPeriodTypeValue != null) { + interestPostingPeriodType = SavingsPostingInterestPeriodType.fromInt(interestPostingPeriodTypeValue); + } else { + interestPostingPeriodType = product.interestPostingPeriodType(); + } + + SavingsInterestCalculationType interestCalculationType = null; + final Integer interestCalculationTypeValue = command.integerValueOfParameterNamed(interestCalculationTypeParamName); + if (interestCalculationTypeValue != null) { + interestCalculationType = SavingsInterestCalculationType.fromInt(interestCalculationTypeValue); + } else { + interestCalculationType = product.interestCalculationType(); + } + + SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType = null; + final Integer interestCalculationDaysInYearTypeValue = command + .integerValueOfParameterNamed(interestCalculationDaysInYearTypeParamName); + if (interestCalculationDaysInYearTypeValue != null) { + interestCalculationDaysInYearType = SavingsInterestCalculationDaysInYearType.fromInt(interestCalculationDaysInYearTypeValue); + } else { + interestCalculationDaysInYearType = product.interestCalculationDaysInYearType(); + } + + BigDecimal minRequiredOpeningBalance = null; + if (command.parameterExists(minRequiredOpeningBalanceParamName)) { + minRequiredOpeningBalance = command.bigDecimalValueOfParameterNamed(minRequiredOpeningBalanceParamName); + } else { + minRequiredOpeningBalance = product.minRequiredOpeningBalance(); + } + + @SuppressWarnings("unused") + Integer lockinPeriodFrequency = null; + if (command.parameterExists(lockinPeriodFrequencyParamName)) { + lockinPeriodFrequency = command.integerValueOfParameterNamed(lockinPeriodFrequencyParamName); + } else { + lockinPeriodFrequency = product.lockinPeriodFrequency(); + } + + SavingsPeriodFrequencyType lockinPeriodFrequencyType = null; + Integer lockinPeriodFrequencyTypeValue = null; + if (command.parameterExists(lockinPeriodFrequencyTypeParamName)) { + lockinPeriodFrequencyTypeValue = command.integerValueOfParameterNamed(lockinPeriodFrequencyTypeParamName); + if (lockinPeriodFrequencyTypeValue != null) { + lockinPeriodFrequencyType = SavingsPeriodFrequencyType.fromInt(lockinPeriodFrequencyTypeValue); + } + } else { + lockinPeriodFrequencyType = product.lockinPeriodFrequencyType(); + } + boolean iswithdrawalFeeApplicableForTransfer = false; + if (command.parameterExists(withdrawalFeeForTransfersParamName)) { + iswithdrawalFeeApplicableForTransfer = command.booleanPrimitiveValueOfParameterNamed(withdrawalFeeForTransfersParamName); + } + + final Set charges = this.savingsAccountChargeAssembler.fromParsedJson(element, product.currency().getCode()); + + DepositAccountInterestRateChart accountChart = null; + InterestRateChart productChart = null; + + if (command.parameterExists(chartIdParamName)) { + Long chartId = command.longValueOfParameterNamed(chartIdParamName); + productChart = product.findChart(chartId); + + } else { + productChart = product.applicableChart(submittedOnDate); + } + + accountChart = DepositAccountInterestRateChart.from(productChart); + + SavingsAccount account = null; + if (depositAccountType.isFixedDeposit()) { + final DepositProductTermAndPreClosure prodTermAndPreClosure = ((FixedDepositProduct) product).depositProductTermAndPreClosure(); + final DepositAccountTermAndPreClosure accountTermAndPreClosure = this.assembleAccountTermAndPreClosure(command, + prodTermAndPreClosure); + + FixedDepositAccount fdAccount = FixedDepositAccount.createNewApplicationForSubmittal(client, group, product, fieldOfficer, + accountNo, externalId, accountType, submittedOnDate, submittedBy, interestRate, interestCompoundingPeriodType, + interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, + lockinPeriodFrequencyTypeValue, lockinPeriodFrequencyType, iswithdrawalFeeApplicableForTransfer, charges, + accountTermAndPreClosure, accountChart); + accountTermAndPreClosure.updateAccountReference(fdAccount); + fdAccount.validateDomainRules(); + account = fdAccount; + } else if (depositAccountType.isRecurringDeposit()) { + final DepositProductTermAndPreClosure prodTermAndPreClosure = ((RecurringDepositProduct) product) + .depositProductTermAndPreClosure(); + final DepositAccountTermAndPreClosure accountTermAndPreClosure = this.assembleAccountTermAndPreClosure(command, + prodTermAndPreClosure); + + final DepositProductRecurringDetail prodRecurringDetail = ((RecurringDepositProduct) product).depositRecurringDetail(); + final DepositAccountRecurringDetail accountRecurringDetail = this.assembleAccountRecurringDetail(command, + prodRecurringDetail.recurringDetail()); + + RecurringDepositAccount rdAccount = RecurringDepositAccount.createNewApplicationForSubmittal(client, group, product, + fieldOfficer, accountNo, externalId, accountType, submittedOnDate, submittedBy, interestRate, + interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, + minRequiredOpeningBalance, lockinPeriodFrequencyTypeValue, lockinPeriodFrequencyType, + iswithdrawalFeeApplicableForTransfer, charges, accountTermAndPreClosure, accountRecurringDetail, accountChart); + + accountTermAndPreClosure.updateAccountReference(rdAccount); + accountRecurringDetail.updateAccountReference(rdAccount); + rdAccount.validateDomainRules(); + account = rdAccount; + } + + if (account != null) { + account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper); + account.validateNewApplicationState(DateUtils.getLocalDateOfTenant(), depositAccountType.resourceName()); + } + + return account; + } + + public SavingsAccount assembleFrom(final Long savingsId, DepositAccountType depositAccountType) { + final SavingsAccount account = this.savingsAccountRepository.findOneWithNotFoundDetection(savingsId, depositAccountType); + account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper); + + return account; + } + + public void assignSavingAccountHelpers(final SavingsAccount savingsAccount) { + savingsAccount.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper); + } + + public DepositAccountTermAndPreClosure assembleAccountTermAndPreClosure(final JsonCommand command, + final DepositProductTermAndPreClosure productTermAndPreclosure) { + final DepositPreClosureDetail productPreClosure = (productTermAndPreclosure == null) ? null : productTermAndPreclosure + .depositPreClosureDetail(); + final DepositTermDetail productTerm = (productTermAndPreclosure == null) ? null : productTermAndPreclosure.depositTermDetail(); + + final DepositPreClosureDetail updatedProductPreClosure = this.depositProductAssembler.assemblePreClosureDetail(command, + productPreClosure); + final DepositTermDetail updatedProductTerm = this.depositProductAssembler.assembleDepositTermDetail(command, productTerm); + + final BigDecimal depositAmount = command.bigDecimalValueOfParameterNamed(depositAmountParamName); + final Integer depositPeriod = command.integerValueOfParameterNamed(depositPeriodParamName); + final Integer depositPeriodFrequencyId = command.integerValueOfParameterNamed(depositPeriodFrequencyIdParamName); + final SavingsPeriodFrequencyType depositPeriodFrequency = SavingsPeriodFrequencyType.fromInt(depositPeriodFrequencyId); + final SavingsAccount account = null; + + // calculate maturity amount + final BigDecimal maturityAmount = null;// calculated and updated in + // account + final LocalDate maturityDate = null;// calculated and updated in account + final DepositAccountOnClosureType accountOnClosureType = null; + return DepositAccountTermAndPreClosure.createNew(updatedProductPreClosure, updatedProductTerm, account, depositAmount, + maturityAmount, maturityDate, depositPeriod, depositPeriodFrequency, accountOnClosureType); + } + + public DepositAccountRecurringDetail assembleAccountRecurringDetail(final JsonCommand command, + final DepositRecurringDetail prodRecurringDetail) { + + RecurringDepositType recurringDepositType = null; + Integer recurringDepositTypeValue = null; + + final BigDecimal recurringDepositAmount = command.bigDecimalValueOfParameterNamed(recurringDepositAmountParamName); + + if (command.parameterExists(recurringDepositTypeIdParamName)) { + recurringDepositTypeValue = command.integerValueOfParameterNamed(recurringDepositTypeIdParamName); + } else { + recurringDepositTypeValue = prodRecurringDetail.recurringDepositType(); + } + + if (recurringDepositTypeValue != null) { + recurringDepositType = RecurringDepositType.fromInt(recurringDepositTypeValue); + } + + SavingsPeriodFrequencyType recurringDepositFrequencyType = null; + Integer recurringDepositFrequencyTypeValue = null; + if (command.parameterExists(recurringDepositFrequencyTypeIdParamName)) { + recurringDepositFrequencyTypeValue = command.integerValueOfParameterNamed(recurringDepositFrequencyTypeIdParamName); + } else { + recurringDepositFrequencyTypeValue = prodRecurringDetail.recurringDepositFrequencyTypeId(); + } + + if (recurringDepositFrequencyTypeValue != null) { + recurringDepositFrequencyType = SavingsPeriodFrequencyType.fromInt(recurringDepositFrequencyTypeValue); + } + + Integer recurringDepositFrequency = null; + if (command.parameterExists(recurringDepositFrequencyTypeIdParamName)) { + recurringDepositFrequency = command.integerValueOfParameterNamed(recurringDepositFrequencyParamName); + } else { + recurringDepositFrequency = prodRecurringDetail.recurringDepositFrequency(); + } + + final DepositRecurringDetail depositRecurringDetail = DepositRecurringDetail.createFrom(recurringDepositType, + recurringDepositFrequency, recurringDepositFrequencyType); + final DepositAccountRecurringDetail depositAccountRecurringDetail = DepositAccountRecurringDetail.createNew(recurringDepositAmount, + depositRecurringDetail, null); + return depositAccountRecurringDetail; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountDomainService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountDomainService.java new file mode 100644 index 000000000..10d09f734 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountDomainService.java @@ -0,0 +1,30 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import java.math.BigDecimal; +import java.util.Map; + +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormatter; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; +import org.mifosplatform.useradministration.domain.AppUser; + +public interface DepositAccountDomainService { + + SavingsAccountTransaction handleWithdrawal(SavingsAccount account, DateTimeFormatter fmt, LocalDate transactionDate, + BigDecimal transactionAmount, PaymentDetail paymentDetail, boolean applyWithdrawFee); + + SavingsAccountTransaction handleDeposit(SavingsAccount account, DateTimeFormatter fmt, LocalDate transactionDate, + BigDecimal transactionAmount, PaymentDetail paymentDetail); + + SavingsAccountTransaction handleAccountClosure(SavingsAccount account, PaymentDetail paymentDetail, AppUser user, JsonCommand command, + LocalDate tenantsTodayDate, Map changes); + + SavingsAccountTransaction handleAccountPreMatureClosure(SavingsAccount account, PaymentDetail paymentDetail, AppUser user, JsonCommand command, + LocalDate tenantsTodayDate, Map changes); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountDomainServiceJpa.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountDomainServiceJpa.java new file mode 100644 index 000000000..202460ae0 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountDomainServiceJpa.java @@ -0,0 +1,320 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.toSavingsAccountIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.transferDescriptionParamName; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.util.Date; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.service.DateUtils; +import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; +import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper; +import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; +import org.mifosplatform.organisation.monetary.domain.Money; +import org.mifosplatform.portfolio.account.domain.AccountTransferDetailRepository; +import org.mifosplatform.portfolio.account.domain.AccountTransferDetails; +import org.mifosplatform.portfolio.account.domain.AccountTransferTransaction; +import org.mifosplatform.portfolio.account.domain.AccountTransferType; +import org.mifosplatform.portfolio.client.domain.AccountNumberGenerator; +import org.mifosplatform.portfolio.client.domain.AccountNumberGeneratorFactory; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionDTO; +import org.mifosplatform.useradministration.domain.AppUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class DepositAccountDomainServiceJpa implements DepositAccountDomainService { + + private final SavingsAccountRepositoryWrapper savingsAccountRepository; + private final SavingsAccountTransactionRepository savingsAccountTransactionRepository; + private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper; + private final JournalEntryWritePlatformService journalEntryWritePlatformService; + private final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory; + private final AccountTransferDetailRepository accountTransferDetailRepository; + private final DepositAccountAssembler depositAccountAssembler; + private final SavingsAccountDomainService savingsAccountDomainService; + + @Autowired + public DepositAccountDomainServiceJpa(final SavingsAccountRepositoryWrapper savingsAccountRepository, + final SavingsAccountTransactionRepository savingsAccountTransactionRepository, + final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper, + final JournalEntryWritePlatformService journalEntryWritePlatformService, + final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory, + final AccountTransferDetailRepository accountTransferDetailRepository, final DepositAccountAssembler depositAccountAssembler, + final SavingsAccountDomainService savingsAccountDomainService) { + this.savingsAccountRepository = savingsAccountRepository; + this.savingsAccountTransactionRepository = savingsAccountTransactionRepository; + this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper; + this.journalEntryWritePlatformService = journalEntryWritePlatformService; + this.accountIdentifierGeneratorFactory = accountIdentifierGeneratorFactory; + this.accountTransferDetailRepository = accountTransferDetailRepository; + this.depositAccountAssembler = depositAccountAssembler; + this.savingsAccountDomainService = savingsAccountDomainService; + } + + @Transactional + @Override + public SavingsAccountTransaction handleWithdrawal(final SavingsAccount account, final DateTimeFormatter fmt, + final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail, + final boolean applyWithdrawFee) { + + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); + final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount, + paymentDetail, new Date()); + final SavingsAccountTransaction withdrawal = account.withdraw(transactionDTO, applyWithdrawFee); + + final MathContext mc = MathContext.DECIMAL64; + if (account.isBeforeLastPostingPeriod(transactionDate)) { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.postInterest(mc, today); + } else { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.calculateInterestUsing(mc, today); + } + account.validateAccountBalanceDoesNotBecomeNegative(transactionAmount); + saveTransactionToGenerateTransactionId(withdrawal); + this.savingsAccountRepository.save(account); + + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + + return withdrawal; + } + + @Transactional + @Override + public SavingsAccountTransaction handleDeposit(final SavingsAccount account, final DateTimeFormatter fmt, + final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail) { + + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); + final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount, + paymentDetail, new Date()); + final SavingsAccountTransaction deposit = account.deposit(transactionDTO); + + final MathContext mc = MathContext.DECIMAL64; + if (account.isBeforeLastPostingPeriod(transactionDate)) { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.postInterest(mc, today); + } else { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.calculateInterestUsing(mc, today); + } + + saveTransactionToGenerateTransactionId(deposit); + + this.savingsAccountRepository.save(account); + + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + + return deposit; + } + + @Transactional + @Override + public SavingsAccountTransaction handleAccountClosure(final SavingsAccount account, final PaymentDetail paymentDetail, + final AppUser user, final JsonCommand command, final LocalDate tenantsTodayDate, final Map changes) { + + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); + + final DepositAccountType depositAccountType = account.depositAccountType(); + SavingsAccountTransaction withdrawal = null; + final MathContext mc = MathContext.DECIMAL64; + if (depositAccountType.isFixedDeposit()) { + final FixedDepositAccount fdAccount = (FixedDepositAccount) account; + withdrawal = fdAccount.close(user, command, tenantsTodayDate, paymentDetail, changes); + final Money transactionAmount = withdrawal.getAmount(fdAccount.getCurrency()); + final LocalDate transactionDate = withdrawal.transactionLocalDate(); + if (fdAccount.isReinvestOnClosure()) { + FixedDepositAccount reinvestedDeposit = fdAccount.reInvest(transactionAmount); + fdAccount.updateMaturityDateAndAmountBeforeAccountActivation(mc); + this.savingsAccountRepository.save(reinvestedDeposit); + autoGenerateAccountNumber(reinvestedDeposit); + } else if (fdAccount.isTransferToSavingsOnClosure()) { + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); + final Long toSavingsId = command.longValueOfParameterNamed(toSavingsAccountIdParamName); + final String transferDescription = command.stringValueOfParameterNamed(transferDescriptionParamName); + final SavingsAccount toSavingsAccount = this.depositAccountAssembler.assembleFrom(toSavingsId, + DepositAccountType.SAVINGS_DEPOSIT); + final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, fmt, + withdrawal.transactionLocalDate(), transactionAmount.getAmount(), null); + + final AccountTransferDetails accountTransferDetails = AccountTransferDetails.savingsToSavingsTransfer(fdAccount.office(), + fdAccount.getClient(), fdAccount, toSavingsAccount.office(), toSavingsAccount.getClient(), toSavingsAccount, + AccountTransferType.ACCOUNT_TRANSFER.getValue()); + AccountTransferTransaction accountTransferTransaction = AccountTransferTransaction.savingsToSavingsTransfer( + accountTransferDetails, withdrawal, deposit, transactionDate, transactionAmount, transferDescription); + + accountTransferDetails.addAccountTransferTransaction(accountTransferTransaction); + + this.accountTransferDetailRepository.save(accountTransferDetails); + } + } else if (depositAccountType.isRecurringDeposit()) { + final RecurringDepositAccount rdAccount = (RecurringDepositAccount) account; + withdrawal = rdAccount.close(user, command, tenantsTodayDate, paymentDetail, changes); + final Money transactionAmount = withdrawal.getAmount(rdAccount.getCurrency()); + final LocalDate transactionDate = withdrawal.transactionLocalDate(); + if (rdAccount.isReinvestOnClosure()) { + RecurringDepositAccount reinvestedDeposit = rdAccount.reInvest(transactionAmount); + final LocalDate transactionStartDate = rdAccount.depositStartDate(); + rdAccount.updateMaturityDateAndAmount(mc, transactionStartDate); + this.savingsAccountRepository.save(reinvestedDeposit); + autoGenerateAccountNumber(reinvestedDeposit); + } else if (rdAccount.isTransferToSavingsOnClosure()) { + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); + final Long toSavingsId = command.longValueOfParameterNamed(toSavingsAccountIdParamName); + final String transferDescription = command.stringValueOfParameterNamed(transferDescriptionParamName); + final SavingsAccount toSavingsAccount = this.depositAccountAssembler.assembleFrom(toSavingsId, + DepositAccountType.SAVINGS_DEPOSIT); + final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, fmt, + withdrawal.transactionLocalDate(), transactionAmount.getAmount(), null); + + final AccountTransferDetails accountTransferDetails = AccountTransferDetails.savingsToSavingsTransfer(rdAccount.office(), + rdAccount.getClient(), rdAccount, toSavingsAccount.office(), toSavingsAccount.getClient(), toSavingsAccount, + AccountTransferType.ACCOUNT_TRANSFER.getValue()); + AccountTransferTransaction accountTransferTransaction = AccountTransferTransaction.savingsToSavingsTransfer( + accountTransferDetails, withdrawal, deposit, transactionDate, transactionAmount, transferDescription); + + accountTransferDetails.addAccountTransferTransaction(accountTransferTransaction); + + this.accountTransferDetailRepository.save(accountTransferDetails); + } + } + + this.savingsAccountRepository.save(account); + if (withdrawal != null) { + saveTransactionToGenerateTransactionId(withdrawal); + } + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + + return withdrawal; + } + + private void autoGenerateAccountNumber(final SavingsAccount account) { + if (account.isAccountNumberRequiresAutoGeneration()) { + final AccountNumberGenerator accountNoGenerator = this.accountIdentifierGeneratorFactory + .determineSavingsAccountNoGenerator(account.getId()); + account.updateAccountNo(accountNoGenerator.generate()); + this.savingsAccountRepository.save(account); + } + } + + @Transactional + @Override + public SavingsAccountTransaction handleAccountPreMatureClosure(final SavingsAccount account, final PaymentDetail paymentDetail, + final AppUser user, final JsonCommand command, final LocalDate tenantsTodayDate, final Map changes) { + + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); + + final DepositAccountType depositAccountType = account.depositAccountType(); + SavingsAccountTransaction withdrawal = null; + if (depositAccountType.isFixedDeposit()) { + final FixedDepositAccount fdAccount = (FixedDepositAccount) account; + withdrawal = fdAccount.prematureClosure(user, command, tenantsTodayDate, paymentDetail, changes); + final Money transactionAmount = withdrawal.getAmount(fdAccount.getCurrency()); + final LocalDate transactionDate = withdrawal.transactionLocalDate(); + if (fdAccount.isTransferToSavingsOnClosure()) { + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); + final Long toSavingsId = command.longValueOfParameterNamed(toSavingsAccountIdParamName); + final String transferDescription = command.stringValueOfParameterNamed(transferDescriptionParamName); + final SavingsAccount toSavingsAccount = this.depositAccountAssembler.assembleFrom(toSavingsId, + DepositAccountType.SAVINGS_DEPOSIT); + final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, fmt, + withdrawal.transactionLocalDate(), transactionAmount.getAmount(), null); + + final AccountTransferDetails accountTransferDetails = AccountTransferDetails.savingsToSavingsTransfer(fdAccount.office(), + fdAccount.getClient(), fdAccount, toSavingsAccount.office(), toSavingsAccount.getClient(), toSavingsAccount, + AccountTransferType.ACCOUNT_TRANSFER.getValue()); + AccountTransferTransaction accountTransferTransaction = AccountTransferTransaction.savingsToSavingsTransfer( + accountTransferDetails, withdrawal, deposit, transactionDate, transactionAmount, transferDescription); + + accountTransferDetails.addAccountTransferTransaction(accountTransferTransaction); + + this.accountTransferDetailRepository.save(accountTransferDetails); + } + } else if (depositAccountType.isRecurringDeposit()) { + final RecurringDepositAccount rdAccount = (RecurringDepositAccount) account; + withdrawal = rdAccount.prematureClosure(user, command, tenantsTodayDate, paymentDetail, changes); + final Money transactionAmount = withdrawal.getAmount(rdAccount.getCurrency()); + final LocalDate transactionDate = withdrawal.transactionLocalDate(); + if (rdAccount.isTransferToSavingsOnClosure()) { + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); + final Long toSavingsId = command.longValueOfParameterNamed(toSavingsAccountIdParamName); + final String transferDescription = command.stringValueOfParameterNamed(transferDescriptionParamName); + final SavingsAccount toSavingsAccount = this.depositAccountAssembler.assembleFrom(toSavingsId, + DepositAccountType.SAVINGS_DEPOSIT); + final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, fmt, + transactionDate, transactionAmount.getAmount(), null); + + final AccountTransferDetails accountTransferDetails = AccountTransferDetails.savingsToSavingsTransfer(rdAccount.office(), + rdAccount.getClient(), rdAccount, toSavingsAccount.office(), toSavingsAccount.getClient(), toSavingsAccount, + AccountTransferType.ACCOUNT_TRANSFER.getValue()); + AccountTransferTransaction accountTransferTransaction = AccountTransferTransaction.savingsToSavingsTransfer( + accountTransferDetails, withdrawal, deposit, transactionDate, transactionAmount, transferDescription); + + accountTransferDetails.addAccountTransferTransaction(accountTransferTransaction); + + this.accountTransferDetailRepository.save(accountTransferDetails); + } + } + if (withdrawal != null) { + saveTransactionToGenerateTransactionId(withdrawal); + } + + this.savingsAccountRepository.save(account); + + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + + return withdrawal; + } + + private Long saveTransactionToGenerateTransactionId(final SavingsAccountTransaction transaction) { + this.savingsAccountTransactionRepository.save(transaction); + return transaction.getId(); + } + + private void updateExistingTransactionsDetails(SavingsAccount account, Set existingTransactionIds, + Set existingReversedTransactionIds) { + existingTransactionIds.addAll(account.findExistingTransactionIds()); + existingReversedTransactionIds.addAll(account.findExistingReversedTransactionIds()); + } + + private void postJournalEntries(final SavingsAccount savingsAccount, final Set existingTransactionIds, + final Set existingReversedTransactionIds) { + + final MonetaryCurrency currency = savingsAccount.getCurrency(); + final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency); + + final Map accountingBridgeData = savingsAccount.deriveAccountingBridgeData(applicationCurrency.toData(), + existingTransactionIds, existingReversedTransactionIds); + this.journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountInterestRateChart.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountInterestRateChart.java new file mode 100644 index 000000000..25fd80bb4 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountInterestRateChart.java @@ -0,0 +1,130 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import java.math.BigDecimal; +import java.util.HashSet; +import java.util.Set; + +import javax.persistence.CascadeType; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; +import javax.persistence.OneToOne; +import javax.persistence.Table; + +import org.joda.time.LocalDate; +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChart; +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChartFields; +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChartSlab; +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "m_savings_account_interest_rate_chart") +public class DepositAccountInterestRateChart extends AbstractPersistable { + + @Embedded + private InterestRateChartFields chartFields; + + @OneToOne + @JoinColumn(name = "savings_account_id", nullable = false) + private SavingsAccount account; + + @OneToMany(mappedBy = "depositAccountInterestRateChart", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) + private Set chartSlabs = new HashSet(); + + protected DepositAccountInterestRateChart() { + // + } + + public static DepositAccountInterestRateChart from(InterestRateChart productChart) { + final Set chartSlabs = productChart.setOfChartSlabs(); + final Set depostiChartSlabs = new HashSet(); + for (InterestRateChartSlab interestRateChartSlab : chartSlabs) { + depostiChartSlabs.add(DepositAccountInterestRateChartSlabs.from(interestRateChartSlab.slabFields(), null)); + } + final DepositAccountInterestRateChart depositChart = new DepositAccountInterestRateChart(productChart.chartFields(), null, depostiChartSlabs); + //update deposit account interest rate chart ference to chart Slabs + depositChart.updateChartSlabsReference(); + return depositChart; + } + + private DepositAccountInterestRateChart(InterestRateChartFields chartFields, SavingsAccount account, + Set chartSlabs) { + this.chartFields = chartFields; + this.account = account; + this.chartSlabs = chartSlabs; + } + + private void updateChartSlabsReference(){ + final Set chartSlabs = setOfChartSlabs(); + for (DepositAccountInterestRateChartSlabs chartSlab : chartSlabs) { + chartSlab.updateChartReference(this); + } + } + + public Set setOfChartSlabs() { + if (this.chartSlabs == null) { + this.chartSlabs = new HashSet(); + } + return this.chartSlabs; + } + + public DepositAccountInterestRateChartSlabs findChartSlab(Long chartSlabId) { + final Set chartSlabs = setOfChartSlabs(); + + for (DepositAccountInterestRateChartSlabs interestRateChartSlab : chartSlabs) { + if (interestRateChartSlab.getId().equals(chartSlabId)) { return interestRateChartSlab; } + } + return null; + } + + /*private boolean removeChartSlab(DepositAccountInterestRateChartSlabs chartSlab) { + final Set chartSlabs = setOfChartSlabs(); + return chartSlabs.remove(chartSlab); + }*/ + + public LocalDate getFromDateAsLocalDate() { + return this.chartFields.getFromDateAsLocalDate(); + } + + public LocalDate getEndDateAsLocalDate() { + return this.chartFields.getEndDateAsLocalDate(); + } + + public Long savingsAccountId() { + return this.account == null ? null : this.account.getId(); + } + + public SavingsAccount savingsAccount() { + return this.account; + } + + /*private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + }*/ + + public InterestRateChartFields chartFields() { + return this.chartFields; + } + + public void updateDepositAccountReference(final SavingsAccount account){ + this.account = account; + } + + public BigDecimal getApplicableInterestRate(@SuppressWarnings("unused") final BigDecimal depositAmount, final LocalDate periodStartDate, final LocalDate periodEndDate){ + BigDecimal effectiveInterestRate = BigDecimal.ZERO; + for (DepositAccountInterestRateChartSlabs slab : setOfChartSlabs()) { + if(slab.slabFields().isBetweenPeriod(periodStartDate, periodEndDate)){ + effectiveInterestRate = slab.slabFields().annualInterestRate(); + } + } + + return effectiveInterestRate; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountInterestRateChartSlabs.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountInterestRateChartSlabs.java new file mode 100644 index 000000000..bf553d707 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountInterestRateChartSlabs.java @@ -0,0 +1,59 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChartSlabFields; +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "m_savings_account_interest_rate_slab") +public class DepositAccountInterestRateChartSlabs extends AbstractPersistable { + + @Embedded + private InterestRateChartSlabFields slabFields; + + @ManyToOne(optional = false) + @JoinColumn(name = "savings_account_interest_rate_chart_id", referencedColumnName = "id", nullable = false) + private DepositAccountInterestRateChart depositAccountInterestRateChart; + + protected DepositAccountInterestRateChartSlabs() { + // + } + + + private DepositAccountInterestRateChartSlabs(InterestRateChartSlabFields slabFields, + DepositAccountInterestRateChart depositAccountInterestRateChart) { + this.slabFields = slabFields; + this.depositAccountInterestRateChart = depositAccountInterestRateChart; + } + + public void setDepositAccountInterestRateChart(DepositAccountInterestRateChart depositAccountInterestRateChart) { + this.depositAccountInterestRateChart = depositAccountInterestRateChart; + } + + public Long savingsProductId() { + return this.depositAccountInterestRateChart.savingsAccountId(); + } + + public InterestRateChartSlabFields slabFields() { + return this.slabFields; + } + + public static DepositAccountInterestRateChartSlabs from(InterestRateChartSlabFields slabFields, + DepositAccountInterestRateChart depositAccountInterestRateChart){ + return new DepositAccountInterestRateChartSlabs(slabFields, depositAccountInterestRateChart); + } + + public void updateChartReference(final DepositAccountInterestRateChart chart){ + this.depositAccountInterestRateChart = chart; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountRecurringDetail.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountRecurringDetail.java new file mode 100644 index 000000000..ff02c61fc --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountRecurringDetail.java @@ -0,0 +1,101 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositAmountParamName; + +import java.math.BigDecimal; +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.persistence.Column; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; +import javax.persistence.Table; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "m_deposit_account_recurring_detail") +public class DepositAccountRecurringDetail extends AbstractPersistable { + + @Column(name = "recurring_deposit_amount", scale = 6, precision = 19, nullable = true) + private BigDecimal recurringDepositAmount; + + @Embedded + private DepositRecurringDetail recurringDetail; + + @OneToOne + @JoinColumn(name = "savings_account_id", nullable = false) + private SavingsAccount account; + + protected DepositAccountRecurringDetail() { + super(); + } + + public static DepositAccountRecurringDetail createNew(final BigDecimal recurringDepositAmount, DepositRecurringDetail recurringDetail, SavingsAccount account) { + + return new DepositAccountRecurringDetail(recurringDepositAmount, recurringDetail, account); + } + + private DepositAccountRecurringDetail(final BigDecimal recurringDepositAmount, DepositRecurringDetail recurringDetail, SavingsAccount account) { + this.recurringDepositAmount = recurringDepositAmount; + this.recurringDetail = recurringDetail; + this.account = account; + } + + public Map update(final JsonCommand command, final DataValidatorBuilder baseDataValidator) { + final Map actualChanges = new LinkedHashMap(10); + if (command.isChangeInBigDecimalParameterNamed(recurringDepositAmountParamName, this.recurringDepositAmount)) { + final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(recurringDepositAmountParamName); + actualChanges.put(recurringDepositAmountParamName, newValue); + this.recurringDepositAmount = newValue; + } + if (this.recurringDetail != null) { + actualChanges.putAll(this.recurringDetail.update(command, baseDataValidator)); + } + return actualChanges; + } + + public DepositRecurringDetail recurringDetail() { + return this.recurringDetail; + } + + public void updateAccountReference(final SavingsAccount account) { + this.account = account; + } + + public Integer recurringDepositType() { + return this.recurringDetail().recurringDepositType(); + } + + public Integer recurringDepositFrequency() { + return this.recurringDetail().recurringDepositFrequency(); + } + + public Integer recurringDepositFrequencyTypeId() { + return this.recurringDetail().recurringDepositFrequencyTypeId(); + } + + public SavingsPeriodFrequencyType recurringDepositFrequencyType() { + return SavingsPeriodFrequencyType.fromInt(this.recurringDepositFrequencyTypeId()); + } + + public BigDecimal recurringDepositAmount(){ + return this.recurringDepositAmount; + } + + public DepositAccountRecurringDetail copy(){ + final BigDecimal recurringDepositAmount = this.recurringDepositAmount; + final DepositRecurringDetail recurringDetail = this.recurringDetail.copy(); + return DepositAccountRecurringDetail.createNew(recurringDepositAmount, recurringDetail, account); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountTermAndPreClosure.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountTermAndPreClosure.java new file mode 100644 index 000000000..2a6d96cb7 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountTermAndPreClosure.java @@ -0,0 +1,238 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositAmountParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositPeriodFrequencyIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositPeriodParamName; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.persistence.Column; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import org.joda.time.Days; +import org.joda.time.LocalDate; +import org.joda.time.Months; +import org.joda.time.Weeks; +import org.joda.time.Years; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.organisation.monetary.domain.Money; +import org.mifosplatform.portfolio.savings.DepositAccountOnClosureType; +import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; +import org.mifosplatform.portfolio.savings.service.SavingsEnumerations; +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "m_deposit_account_term_and_preclosure") +public class DepositAccountTermAndPreClosure extends AbstractPersistable { + + @Column(name = "deposit_amount", scale = 6, precision = 19, nullable = true) + private BigDecimal depositAmount; + + @Column(name = "maturity_amount", scale = 6, precision = 19, nullable = true) + private BigDecimal maturityAmount; + + @Temporal(TemporalType.DATE) + @Column(name = "maturity_date", nullable = true) + private Date maturityDate; + + @Column(name = "deposit_period", nullable = true) + private Integer depositPeriod; + + @Column(name = "deposit_period_frequency_enum", nullable = true) + private Integer depositPeriodFrequency; + + @Column(name = "on_account_closure_enum", nullable = false) + private Integer onAccountClosureType; + + @Embedded + private DepositPreClosureDetail preClosureDetail; + + @Embedded + protected DepositTermDetail depositTermDetail; + + @OneToOne + @JoinColumn(name = "savings_account_id", nullable = false) + private SavingsAccount account; + + protected DepositAccountTermAndPreClosure() { + super(); + } + + public static DepositAccountTermAndPreClosure createNew(DepositPreClosureDetail preClosureDetail, DepositTermDetail depositTermDetail, + SavingsAccount account, BigDecimal depositAmount, BigDecimal maturityAmount, final LocalDate maturityDate, + Integer depositPeriod, final SavingsPeriodFrequencyType depositPeriodFrequency, + final DepositAccountOnClosureType accountOnClosureType) { + + return new DepositAccountTermAndPreClosure(preClosureDetail, depositTermDetail, account, depositAmount, maturityAmount, + maturityDate, depositPeriod, depositPeriodFrequency, accountOnClosureType); + } + + private DepositAccountTermAndPreClosure(DepositPreClosureDetail preClosureDetail, DepositTermDetail depositTermDetail, + SavingsAccount account, BigDecimal depositAmount, BigDecimal maturityAmount, final LocalDate maturityDate, + Integer depositPeriod, final SavingsPeriodFrequencyType depositPeriodFrequency, + final DepositAccountOnClosureType accountOnClosureType) { + this.depositAmount = depositAmount; + this.maturityAmount = maturityAmount; + this.maturityDate = (maturityDate == null) ? null : maturityDate.toDate(); + this.depositPeriod = depositPeriod; + this.depositPeriodFrequency = (depositPeriodFrequency == null) ? null : depositPeriodFrequency.getValue(); + this.preClosureDetail = preClosureDetail; + this.depositTermDetail = depositTermDetail; + this.account = account; + this.onAccountClosureType = (accountOnClosureType == null) ? null : accountOnClosureType.getValue(); + } + + public Map update(final JsonCommand command, final DataValidatorBuilder baseDataValidator) { + final Map actualChanges = new LinkedHashMap(10); + + if (command.isChangeInBigDecimalParameterNamed(depositAmountParamName, this.depositAmount)) { + final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(depositAmountParamName); + actualChanges.put(depositAmountParamName, newValue); + this.depositAmount = newValue; + } + + if (command.isChangeInIntegerParameterNamed(depositPeriodParamName, this.depositPeriod)) { + final Integer newValue = command.integerValueOfParameterNamed(depositPeriodParamName); + actualChanges.put(depositPeriodParamName, newValue); + this.depositPeriod = newValue; + } + + if (command.isChangeInIntegerParameterNamed(depositPeriodFrequencyIdParamName, this.depositPeriodFrequency)) { + final Integer newValue = command.integerValueOfParameterNamed(depositPeriodFrequencyIdParamName); + actualChanges.put(depositPeriodFrequencyIdParamName, SavingsEnumerations.depositTermFrequencyType(newValue)); + this.depositPeriodFrequency = newValue; + } + + if (this.preClosureDetail != null) { + actualChanges.putAll(this.preClosureDetail.update(command, baseDataValidator)); + } + + if (this.depositTermDetail != null) { + actualChanges.putAll(this.depositTermDetail.update(command, baseDataValidator)); + } + return actualChanges; + } + + public DepositPreClosureDetail depositPreClosureDetail() { + return this.preClosureDetail; + } + + public DepositTermDetail depositTermDetail() { + return this.depositTermDetail; + } + + public BigDecimal depositAmount() { + return this.depositAmount; + } + + public Integer depositPeriod() { + return this.depositPeriod; + } + + public Integer depositPeriodFrequency() { + return this.depositPeriodFrequency; + } + + public SavingsPeriodFrequencyType depositPeriodFrequencyType() { + return SavingsPeriodFrequencyType.fromInt(depositPeriodFrequency); + } + + public void updateAccountReference(final SavingsAccount account) { + this.account = account; + } + + public void updateMaturityDetails(final BigDecimal maturityAmount, final LocalDate maturityDate) { + this.maturityAmount = maturityAmount; + this.maturityDate = maturityDate.toDate(); + } + + public void updateMaturityDetails(final BigDecimal depositAmount, final BigDecimal interestPayable, final LocalDate maturityDate) { + this.depositAmount = depositAmount; + this.maturityAmount = this.depositAmount.add(interestPayable); + this.maturityDate = maturityDate.toDate(); + } + + public LocalDate getMaturityLocalDate() { + LocalDate maturityLocalDate = null; + if (this.maturityDate != null) { + maturityLocalDate = new LocalDate(this.maturityDate); + } + return maturityLocalDate; + } + + public boolean isPreClosurePenalApplicable() { + if (this.preClosureDetail != null) { return this.preClosureDetail.preClosurePenalApplicable(); } + return false; + } + + public Integer getActualDepositPeriod(final LocalDate interestPostingUpToDate, final SavingsPeriodFrequencyType periodFrequencyType) { + final LocalDate depositFromDate = this.account.accountSubmittedOrActivationDate(); + Integer actualDepositPeriod = this.depositPeriod; + if (depositFromDate == null || getMaturityLocalDate() == null || interestPostingUpToDate.isEqual(getMaturityLocalDate())) { return actualDepositPeriod; } + + final SavingsPeriodFrequencyType depositPeriodFrequencyType = periodFrequencyType; + switch (depositPeriodFrequencyType) { + case DAYS: + actualDepositPeriod = Days.daysBetween(depositFromDate, interestPostingUpToDate).getDays(); + break; + case WEEKS: + actualDepositPeriod = Weeks.weeksBetween(depositFromDate, interestPostingUpToDate).getWeeks(); + break; + case MONTHS: + actualDepositPeriod = Months.monthsBetween(depositFromDate, interestPostingUpToDate).getMonths(); + break; + case YEARS: + actualDepositPeriod = Years.yearsBetween(depositFromDate, interestPostingUpToDate).getYears(); + break; + case INVALID: + actualDepositPeriod = this.depositPeriod;// default value + break; + } + return actualDepositPeriod; + } + + public BigDecimal maturityAmount() { + return this.maturityAmount; + } + + public void updateOnAccountClosureStatus(final DepositAccountOnClosureType onClosureType) { + this.onAccountClosureType = onClosureType.getValue(); + } + + public boolean isReinvestOnClosure() { + return DepositAccountOnClosureType.fromInt(this.onAccountClosureType).isReinvest(); + } + + public boolean isTransferToSavingsOnClosure() { + return DepositAccountOnClosureType.fromInt(this.onAccountClosureType).isTransferToSavings(); + } + + public DepositAccountTermAndPreClosure deepCopy(Money depositAmount) { + final SavingsAccount account = null; + final BigDecimal maturityAmount = null; + final LocalDate maturityDate = null; + final Integer depositPeriod = this.depositPeriod; + final SavingsPeriodFrequencyType depositPeriodFrequency = SavingsPeriodFrequencyType.fromInt(this.depositPeriodFrequency); + final DepositPreClosureDetail preClosureDetail = this.preClosureDetail.copy(); + final DepositTermDetail depositTermDetail = this.depositTermDetail.copy(); + + final DepositAccountOnClosureType accountOnClosureType = null; + return DepositAccountTermAndPreClosure.createNew(preClosureDetail, depositTermDetail, account, depositAmount.getAmount(), maturityAmount, + maturityDate, depositPeriod, depositPeriodFrequency, accountOnClosureType); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositPreClosureDetail.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositPreClosureDetail.java new file mode 100644 index 000000000..6a077a05f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositPreClosureDetail.java @@ -0,0 +1,231 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.interestFreeFromPeriodParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.interestFreePeriodApplicableParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.interestFreePeriodFrequencyTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.interestFreeToPeriodParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.preClosurePenalApplicableParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.preClosurePenalInterestOnTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.preClosurePenalInterestParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.localeParamName; + +import java.math.BigDecimal; +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.persistence.Column; +import javax.persistence.Embeddable; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.portfolio.interestratechart.InterestRateChartPeriodType; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartEnumerations; +import org.mifosplatform.portfolio.savings.PreClosurePenalInterestOnType; +import org.mifosplatform.portfolio.savings.service.SavingsEnumerations; + +/** + * DepositPreClosureDetail encapsulates all the details of a + * {@link FixedDepositProduct} that are also used and persisted by a + * {@link FixedDepositAccount}. + */ +@Embeddable +public class DepositPreClosureDetail { + + @Column(name = "interest_free_period_applicable") + private boolean interestFreePeriodApplicable; + + @Column(name = "pre_closure_penal_applicable") + private boolean preClosurePenalApplicable; + + @Column(name = "interest_free_from_period", nullable = true) + private Integer interestFreeFromPeriod; + + @Column(name = "interest_free_to_period", nullable = true) + private Integer interestFreeToPeriod; + + @Column(name = "interest_free_period_frequency_enum", nullable = true) + private Integer interestFreePeriodFrequencyType; + + @Column(name = "pre_closure_penal_interest", scale = 6, precision = 19, nullable = true) + private BigDecimal preClosurePenalInterest; + + @Column(name = "pre_closure_penal_interest_on_enum", nullable = true) + private Integer preClosurePenalInterestOnType; + + public static DepositPreClosureDetail createFrom(final boolean interestFreePeriodApplicable, final Integer interestFreeFromPeriod, + final Integer interestFreeToPeriod, final InterestRateChartPeriodType periodType, final boolean preClosurePenalApplicable, + final BigDecimal preClosurePenalInterest, final PreClosurePenalInterestOnType preClosurePenalInterestType) { + + return new DepositPreClosureDetail(interestFreePeriodApplicable, interestFreeFromPeriod, interestFreeToPeriod, periodType, + preClosurePenalApplicable, preClosurePenalInterest, preClosurePenalInterestType); + } + + protected DepositPreClosureDetail() { + // + } + + private DepositPreClosureDetail(final boolean interestFreePeriodApplicable, final Integer interestFreeFromPeriod, + final Integer interestFreeToPeriod, final InterestRateChartPeriodType periodType, final boolean preClosurePenalApplicable, + final BigDecimal preClosurePenalInterest, final PreClosurePenalInterestOnType preClosurePenalInterestType) { + this.interestFreePeriodApplicable = interestFreePeriodApplicable; + this.interestFreeFromPeriod = interestFreeFromPeriod; + this.interestFreeToPeriod = interestFreeToPeriod; + this.interestFreePeriodFrequencyType = (periodType == null) ? null : periodType.getValue(); + this.preClosurePenalApplicable = preClosurePenalApplicable; + this.preClosurePenalInterest = preClosurePenalInterest; + this.preClosurePenalInterestOnType = (preClosurePenalInterestType == null) ? null : preClosurePenalInterestType.getValue(); + } + + protected Map update(final JsonCommand command, final DataValidatorBuilder baseDataValidator) { + final Map actualChanges = new LinkedHashMap(10); + + final String localeAsInput = command.locale(); + + if (command.isChangeInBooleanParameterNamed(interestFreePeriodApplicableParamName, this.interestFreePeriodApplicable)) { + final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(interestFreePeriodApplicableParamName); + actualChanges.put(interestFreePeriodApplicableParamName, newValue); + this.interestFreePeriodApplicable = newValue; + } + + if (this.interestFreePeriodApplicable) { + + if (command.isChangeInIntegerParameterNamed(interestFreeFromPeriodParamName, this.interestFreeFromPeriod)) { + final Integer newValue = command.integerValueOfParameterNamed(interestFreeFromPeriodParamName); + actualChanges.put(interestFreeFromPeriodParamName, newValue); + this.interestFreeFromPeriod = newValue; + } + + if (command.isChangeInIntegerParameterNamed(interestFreeToPeriodParamName, this.interestFreeToPeriod)) { + final Integer newValue = command.integerValueOfParameterNamed(interestFreeToPeriodParamName); + actualChanges.put(interestFreeToPeriodParamName, newValue); + this.interestFreeToPeriod = newValue; + } + + if (command.isChangeInIntegerParameterNamed(interestFreePeriodFrequencyTypeIdParamName, this.interestFreePeriodFrequencyType)) { + final Integer newValue = command.integerValueOfParameterNamed(interestFreePeriodFrequencyTypeIdParamName); + actualChanges.put(interestFreePeriodFrequencyTypeIdParamName, InterestRateChartEnumerations.periodType(newValue)); + this.interestFreePeriodFrequencyType = newValue; + } + + if (this.interestFreeFromPeriod == null) { + baseDataValidator.parameter(interestFreeFromPeriodParamName).value(this.interestFreeFromPeriod) + .cantBeBlankWhenParameterProvidedIs(interestFreePeriodApplicableParamName, this.interestFreePeriodApplicable); + } + + if (this.interestFreeToPeriod == null) { + baseDataValidator.parameter(interestFreeToPeriodParamName).value(this.interestFreeToPeriod) + .cantBeBlankWhenParameterProvidedIs(interestFreePeriodApplicableParamName, this.interestFreePeriodApplicable); + } + + if (this.interestFreePeriodFrequencyType == null) { + baseDataValidator.parameter(interestFreePeriodFrequencyTypeIdParamName).value(this.interestFreePeriodFrequencyType) + .cantBeBlankWhenParameterProvidedIs(interestFreePeriodApplicableParamName, this.interestFreePeriodApplicable); + } + } else { // reset if interest free period is not applicable + this.interestFreeFromPeriod = null; + this.interestFreeToPeriod = null; + this.interestFreePeriodFrequencyType = null; + } + + if (command.isChangeInBooleanParameterNamed(preClosurePenalApplicableParamName, this.preClosurePenalApplicable)) { + final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(preClosurePenalApplicableParamName); + actualChanges.put(preClosurePenalApplicableParamName, newValue); + this.preClosurePenalApplicable = newValue; + } + + if (this.preClosurePenalApplicable) { + if (command.isChangeInBigDecimalParameterNamed(preClosurePenalInterestParamName, this.preClosurePenalInterest)) { + final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(preClosurePenalInterestParamName); + actualChanges.put(preClosurePenalInterestParamName, newValue); + actualChanges.put(localeParamName, localeAsInput); + this.preClosurePenalInterest = newValue; + } + + if (command.isChangeInIntegerParameterNamed(preClosurePenalInterestOnTypeIdParamName, this.preClosurePenalInterestOnType)) { + final Integer newValue = command.integerValueOfParameterNamed(preClosurePenalInterestOnTypeIdParamName); + actualChanges.put(preClosurePenalInterestOnTypeIdParamName, SavingsEnumerations.preClosurePenaltyInterestOnType(newValue)); + actualChanges.put(localeParamName, localeAsInput); + this.preClosurePenalInterestOnType = newValue; + } + + if (this.preClosurePenalInterest == null) { + baseDataValidator.parameter(preClosurePenalInterestParamName).value(this.preClosurePenalInterest) + .cantBeBlankWhenParameterProvidedIs(preClosurePenalApplicableParamName, this.preClosurePenalApplicable); + } + + if (this.preClosurePenalInterestOnType == null) { + baseDataValidator.parameter(preClosurePenalInterestOnTypeIdParamName).value(this.preClosurePenalInterestOnType) + .cantBeBlankWhenParameterProvidedIs(preClosurePenalApplicableParamName, this.preClosurePenalApplicable); + } + } else { // reset if pre-closure penal interest is not applicable + this.preClosurePenalInterest = null; + this.preClosurePenalInterestOnType = null; + } + + if (isFromPeriodAfterToPeriod()) { + baseDataValidator + .parameter(interestFreeFromPeriodParamName) + .value(this.interestFreeFromPeriod) + .failWithCode("interest.free.from.period.is.greater.than.to.period", this.interestFreeFromPeriod, + this.interestFreeToPeriod); + } + + return actualChanges; + } + + private boolean isFromPeriodAfterToPeriod() { + if (this.interestFreePeriodApplicable && this.interestFreeFromPeriod != null && this.interestFreeToPeriod != null) { + if (this.interestFreeFromPeriod.compareTo(interestFreeToPeriod) > 0) { return true; } + } + return false; + } + + public boolean interestFreePeriodApplicable() { + return this.interestFreePeriodApplicable; + } + + public boolean preClosurePenalApplicable() { + return this.preClosurePenalApplicable; + } + + public Integer interestFreeFromPeriod() { + return this.interestFreeFromPeriod; + } + + public Integer interestFreeToPeriod() { + return this.interestFreeToPeriod; + } + + public Integer interestFreePeriodFrequencyType() { + return this.interestFreePeriodFrequencyType; + } + + public BigDecimal preClosurePenalInterest() { + return this.preClosurePenalInterest; + } + + public Integer preClosurePenalInterestOnTypeId() { + return this.preClosurePenalInterestOnType; + } + + public PreClosurePenalInterestOnType preClosurePenalInterestOnType(){ + return PreClosurePenalInterestOnType.fromInt(preClosurePenalInterestOnType); + } + + public DepositPreClosureDetail copy(){ + final boolean interestFreePeriodApplicable = this.interestFreePeriodApplicable; + final Integer interestFreeFromPeriod = this.interestFreeFromPeriod; + final Integer interestFreeToPeriod = this.interestFreeToPeriod; + final boolean preClosurePenalApplicable = this.preClosurePenalApplicable; + final BigDecimal preClosurePenalInterest = this.preClosurePenalInterest; + final InterestRateChartPeriodType periodType = InterestRateChartPeriodType.fromInt(this.interestFreePeriodFrequencyType); + final PreClosurePenalInterestOnType preClosurePenalInterestType = PreClosurePenalInterestOnType.fromInt(this.preClosurePenalInterestOnType); + + return DepositPreClosureDetail.createFrom(interestFreePeriodApplicable, interestFreeFromPeriod, interestFreeToPeriod, periodType, preClosurePenalApplicable, preClosurePenalInterest, preClosurePenalInterestType); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositProductAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositProductAssembler.java new file mode 100644 index 000000000..cfe81b91f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositProductAssembler.java @@ -0,0 +1,538 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.chartsParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.inMultiplesOfDepositTermParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.inMultiplesOfDepositTermTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.interestFreeFromPeriodParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.interestFreePeriodApplicableParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.interestFreePeriodFrequencyTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.interestFreeToPeriodParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.maxDepositTermParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.maxDepositTermTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.minDepositTermParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.minDepositTermTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.preClosurePenalApplicableParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.preClosurePenalInterestOnTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.preClosurePenalInterestParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositFrequencyParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositFrequencyTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositTypeIdParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargesParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.currencyCodeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.descriptionParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.idParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.inMultiplesOfParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nameParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.shortNameParamName; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.mifosplatform.accounting.common.AccountingRuleType; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; +import org.mifosplatform.portfolio.charge.domain.Charge; +import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper; +import org.mifosplatform.portfolio.charge.exception.ChargeCannotBeAppliedToException; +import org.mifosplatform.portfolio.interestratechart.InterestRateChartPeriodType; +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChart; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartAssembler; +import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyException; +import org.mifosplatform.portfolio.savings.PreClosurePenalInterestOnType; +import org.mifosplatform.portfolio.savings.RecurringDepositType; +import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; +import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; +import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +@Component +public class DepositProductAssembler { + + private final ChargeRepositoryWrapper chargeRepository; + private final InterestRateChartAssembler chartAssembler; + + @Autowired + public DepositProductAssembler(final ChargeRepositoryWrapper chargeRepository, final InterestRateChartAssembler chartAssembler) { + this.chargeRepository = chargeRepository; + this.chartAssembler = chartAssembler; + } + + public FixedDepositProduct assembleFixedDepositProduct(final JsonCommand command) { + + final String name = command.stringValueOfParameterNamed(nameParamName); + final String shortName = command.stringValueOfParameterNamed(shortNameParamName); + final String description = command.stringValueOfParameterNamed(descriptionParamName); + + final String currencyCode = command.stringValueOfParameterNamed(currencyCodeParamName); + final Integer digitsAfterDecimal = command.integerValueOfParameterNamed(digitsAfterDecimalParamName); + final Integer inMultiplesOf = command.integerValueOfParameterNamed(inMultiplesOfParamName); + final MonetaryCurrency currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal, inMultiplesOf); + + BigDecimal interestRate = command.bigDecimalValueOfParameterNamed(nominalAnnualInterestRateParamName); + + SavingsCompoundingInterestPeriodType interestCompoundingPeriodType = null; + final Integer interestPeriodTypeValue = command.integerValueOfParameterNamed(interestCompoundingPeriodTypeParamName); + if (interestPeriodTypeValue != null) { + interestCompoundingPeriodType = SavingsCompoundingInterestPeriodType.fromInt(interestPeriodTypeValue); + } + + SavingsPostingInterestPeriodType interestPostingPeriodType = null; + final Integer interestPostingPeriodTypeValue = command.integerValueOfParameterNamed(interestPostingPeriodTypeParamName); + if (interestPostingPeriodTypeValue != null) { + interestPostingPeriodType = SavingsPostingInterestPeriodType.fromInt(interestPostingPeriodTypeValue); + } + + SavingsInterestCalculationType interestCalculationType = null; + final Integer interestCalculationTypeValue = command.integerValueOfParameterNamed(interestCalculationTypeParamName); + if (interestCalculationTypeValue != null) { + interestCalculationType = SavingsInterestCalculationType.fromInt(interestCalculationTypeValue); + } + + SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType = null; + final Integer interestCalculationDaysInYearTypeValue = command + .integerValueOfParameterNamed(interestCalculationDaysInYearTypeParamName); + if (interestCalculationDaysInYearTypeValue != null) { + interestCalculationDaysInYearType = SavingsInterestCalculationDaysInYearType.fromInt(interestCalculationDaysInYearTypeValue); + } + + final Integer lockinPeriodFrequency = command.integerValueOfParameterNamedDefaultToNullIfZero(lockinPeriodFrequencyParamName); + SavingsPeriodFrequencyType lockinPeriodFrequencyType = null; + final Integer lockinPeriodFrequencyTypeValue = command.integerValueOfParameterNamed(lockinPeriodFrequencyTypeParamName); + if (lockinPeriodFrequencyTypeValue != null) { + lockinPeriodFrequencyType = SavingsPeriodFrequencyType.fromInt(lockinPeriodFrequencyTypeValue); + } + + final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(command.integerValueOfParameterNamed("accountingRule")); + + final DepositPreClosureDetail preClosureDetail = this.assemblePreClosureDetail(command); + final DepositTermDetail depositTermDetail = this.assembleDepositTermDetail(command); + final DepositProductTermAndPreClosure productTermAndPreClosure = DepositProductTermAndPreClosure.createNew(preClosureDetail, + depositTermDetail, null); + + // Savings product charges + final Set charges = assembleListOfSavingsProductCharges(command, currencyCode); + // Interest rate charts + final Set charts = assembleListOfCharts(command, currency.getCode()); + if (interestRate == null) { + interestRate = BigDecimal.ZERO; + } + FixedDepositProduct fixedDepositProduct = FixedDepositProduct.createNew(name, shortName, description, currency, interestRate, + interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, + lockinPeriodFrequency, lockinPeriodFrequencyType, accountingRuleType, charges, productTermAndPreClosure, charts); + + // update product reference + productTermAndPreClosure.updateProductReference(fixedDepositProduct); + + validateFixedDepositProductDomainRules(fixedDepositProduct); + + return fixedDepositProduct; + } + + private void validateFixedDepositProductDomainRules(FixedDepositProduct fixedDepositProduct) { + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME); + final DepositTermDetail termDetails = fixedDepositProduct.depositProductTermAndPreClosure().depositTermDetail(); + final boolean isMinTermGreaterThanMax = termDetails.isMinDepositTermGreaterThanMaxDepositTerm(); + if (isMinTermGreaterThanMax) { + final Integer maxTerm = termDetails.maxDepositTerm(); + baseDataValidator.reset().parameter(maxDepositTermParamName).value(maxTerm) + .failWithCodeNoParameterAddedToErrorCode("max.term.lessthan.min.term"); + } + fixedDepositProduct.validateCharts(baseDataValidator); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public RecurringDepositProduct assembleRecurringDepositProduct(final JsonCommand command) { + + final String name = command.stringValueOfParameterNamed(nameParamName); + final String shortName = command.stringValueOfParameterNamed(shortNameParamName); + final String description = command.stringValueOfParameterNamed(descriptionParamName); + + final String currencyCode = command.stringValueOfParameterNamed(currencyCodeParamName); + final Integer digitsAfterDecimal = command.integerValueOfParameterNamed(digitsAfterDecimalParamName); + final Integer inMultiplesOf = command.integerValueOfParameterNamed(inMultiplesOfParamName); + final MonetaryCurrency currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal, inMultiplesOf); + + BigDecimal interestRate = command.bigDecimalValueOfParameterNamed(nominalAnnualInterestRateParamName); + + SavingsCompoundingInterestPeriodType interestCompoundingPeriodType = null; + final Integer interestPeriodTypeValue = command.integerValueOfParameterNamed(interestCompoundingPeriodTypeParamName); + if (interestPeriodTypeValue != null) { + interestCompoundingPeriodType = SavingsCompoundingInterestPeriodType.fromInt(interestPeriodTypeValue); + } + + SavingsPostingInterestPeriodType interestPostingPeriodType = null; + final Integer interestPostingPeriodTypeValue = command.integerValueOfParameterNamed(interestPostingPeriodTypeParamName); + if (interestPostingPeriodTypeValue != null) { + interestPostingPeriodType = SavingsPostingInterestPeriodType.fromInt(interestPostingPeriodTypeValue); + } + + SavingsInterestCalculationType interestCalculationType = null; + final Integer interestCalculationTypeValue = command.integerValueOfParameterNamed(interestCalculationTypeParamName); + if (interestCalculationTypeValue != null) { + interestCalculationType = SavingsInterestCalculationType.fromInt(interestCalculationTypeValue); + } + + SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType = null; + final Integer interestCalculationDaysInYearTypeValue = command + .integerValueOfParameterNamed(interestCalculationDaysInYearTypeParamName); + if (interestCalculationDaysInYearTypeValue != null) { + interestCalculationDaysInYearType = SavingsInterestCalculationDaysInYearType.fromInt(interestCalculationDaysInYearTypeValue); + } + + final Integer lockinPeriodFrequency = command.integerValueOfParameterNamedDefaultToNullIfZero(lockinPeriodFrequencyParamName); + SavingsPeriodFrequencyType lockinPeriodFrequencyType = null; + final Integer lockinPeriodFrequencyTypeValue = command.integerValueOfParameterNamed(lockinPeriodFrequencyTypeParamName); + if (lockinPeriodFrequencyTypeValue != null) { + lockinPeriodFrequencyType = SavingsPeriodFrequencyType.fromInt(lockinPeriodFrequencyTypeValue); + } + + final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(command.integerValueOfParameterNamed("accountingRule")); + + final DepositPreClosureDetail preClosureDetail = this.assemblePreClosureDetail(command); + final DepositTermDetail depositTermDetail = this.assembleDepositTermDetail(command); + final DepositProductTermAndPreClosure productTermAndPreClosure = DepositProductTermAndPreClosure.createNew(preClosureDetail, + depositTermDetail, null); + final DepositRecurringDetail recurringDetail = this.assembleRecurringDetail(command); + final DepositProductRecurringDetail productRecurringDetail = DepositProductRecurringDetail.createNew(recurringDetail, null); + + // Savings product charges + final Set charges = assembleListOfSavingsProductCharges(command, currencyCode); + // Interest rate charts + final Set charts = assembleListOfCharts(command, currency.getCode()); + + if (interestRate == null) { + interestRate = BigDecimal.ZERO; + } + + RecurringDepositProduct recurringDepositProduct = RecurringDepositProduct.createNew(name, shortName, description, currency, + interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, + interestCalculationDaysInYearType, lockinPeriodFrequency, lockinPeriodFrequencyType, accountingRuleType, charges, + productTermAndPreClosure, productRecurringDetail, charts); + + // update product reference + productTermAndPreClosure.updateProductReference(recurringDepositProduct); + productRecurringDetail.updateProductReference(recurringDepositProduct); + + validateRecurringDepositProductDomainRules(recurringDepositProduct); + + return recurringDepositProduct; + } + + private void validateRecurringDepositProductDomainRules(RecurringDepositProduct recurringDepositProduct) { + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME); + final DepositTermDetail termDetails = recurringDepositProduct.depositProductTermAndPreClosure().depositTermDetail(); + final boolean isMinTermGreaterThanMax = termDetails.isMinDepositTermGreaterThanMaxDepositTerm(); + if (isMinTermGreaterThanMax) { + final Integer maxTerm = termDetails.maxDepositTerm(); + baseDataValidator.reset().parameter(maxDepositTermParamName).value(maxTerm) + .failWithCodeNoParameterAddedToErrorCode("max.term.lessthan.min.term"); + } + recurringDepositProduct.validateCharts(baseDataValidator); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public DepositPreClosureDetail assemblePreClosureDetail(final JsonCommand command) { + + boolean interestFreePeriodApplicable = false; + Integer interestFreeFromPeriod = null; + Integer interestFreeToPeriod = null; + InterestRateChartPeriodType interestFreePeriodFrequencyType = null; + + if (command.parameterExists(interestFreePeriodApplicableParamName)) { + interestFreePeriodApplicable = command.booleanObjectValueOfParameterNamed(interestFreePeriodApplicableParamName); + if (interestFreePeriodApplicable) { + interestFreeFromPeriod = command.integerValueOfParameterNamed(interestFreeFromPeriodParamName); + interestFreeToPeriod = command.integerValueOfParameterNamed(interestFreeToPeriodParamName); + final Integer interestFreePreriodTypeId = command.integerValueOfParameterNamed(interestFreePeriodFrequencyTypeIdParamName); + interestFreePeriodFrequencyType = (interestFreePreriodTypeId == null) ? null : InterestRateChartPeriodType + .fromInt(interestFreePreriodTypeId); + } + } + + boolean preClosurePenalApplicable = false; + BigDecimal preClosurePenalInterest = null; + PreClosurePenalInterestOnType preClosurePenalInterestType = null; + + if (command.parameterExists(preClosurePenalApplicableParamName)) { + preClosurePenalApplicable = command.booleanObjectValueOfParameterNamed(preClosurePenalApplicableParamName); + if (preClosurePenalApplicable) { + preClosurePenalInterest = command.bigDecimalValueOfParameterNamed(preClosurePenalInterestParamName); + final Integer preClosurePenalInterestOnTypeId = command + .integerValueOfParameterNamed(preClosurePenalInterestOnTypeIdParamName); + preClosurePenalInterestType = preClosurePenalInterestOnTypeId == null ? null : PreClosurePenalInterestOnType + .fromInt(preClosurePenalInterestOnTypeId); + } + } + + DepositPreClosureDetail preClosureDetail = DepositPreClosureDetail.createFrom(interestFreePeriodApplicable, interestFreeFromPeriod, + interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest, + preClosurePenalInterestType); + + return preClosureDetail; + } + + public DepositPreClosureDetail assemblePreClosureDetail(final JsonCommand command, DepositPreClosureDetail produPreClosureDetail) { + + boolean interestFreePeriodApplicable = false; + Integer interestFreeFromPeriod = null; + Integer interestFreeToPeriod = null; + InterestRateChartPeriodType interestFreePeriodFrequencyType = null; + Integer interestFreePreriodTypeId = null; + + if (command.parameterExists(interestFreePeriodApplicableParamName)) { + interestFreePeriodApplicable = command.booleanObjectValueOfParameterNamed(interestFreePeriodApplicableParamName); + if (interestFreePeriodApplicable) { + if (command.parameterExists(interestFreeFromPeriodParamName)) { + interestFreeFromPeriod = command.integerValueOfParameterNamed(interestFreeFromPeriodParamName); + } else { + interestFreeFromPeriod = produPreClosureDetail.interestFreeFromPeriod(); + } + + if (command.parameterExists(interestFreeToPeriodParamName)) { + interestFreeToPeriod = command.integerValueOfParameterNamed(interestFreeToPeriodParamName); + } else { + interestFreeToPeriod = produPreClosureDetail.interestFreeToPeriod(); + } + + if (command.parameterExists(interestFreePeriodFrequencyTypeIdParamName)) { + interestFreePreriodTypeId = command.integerValueOfParameterNamed(interestFreePeriodFrequencyTypeIdParamName); + } else { + interestFreePreriodTypeId = produPreClosureDetail.interestFreePeriodFrequencyType(); + } + interestFreePeriodFrequencyType = (interestFreePreriodTypeId == null) ? null : InterestRateChartPeriodType + .fromInt(interestFreePreriodTypeId); + } + } else { + interestFreePeriodApplicable = produPreClosureDetail.interestFreePeriodApplicable(); + interestFreeFromPeriod = produPreClosureDetail.interestFreeFromPeriod(); + interestFreeToPeriod = produPreClosureDetail.interestFreeToPeriod(); + interestFreePreriodTypeId = produPreClosureDetail.interestFreePeriodFrequencyType(); + interestFreePeriodFrequencyType = (interestFreePreriodTypeId == null) ? null : InterestRateChartPeriodType + .fromInt(interestFreePreriodTypeId); + } + + boolean preClosurePenalApplicable = false; + BigDecimal preClosurePenalInterest = null; + PreClosurePenalInterestOnType preClosurePenalInterestType = null; + Integer preClosurePenalInterestOnTypeId = null; + if (command.parameterExists(preClosurePenalApplicableParamName)) { + preClosurePenalApplicable = command.booleanObjectValueOfParameterNamed(preClosurePenalApplicableParamName); + if (preClosurePenalApplicable) { + if (command.parameterExists(preClosurePenalInterestParamName)) { + preClosurePenalInterest = command.bigDecimalValueOfParameterNamed(preClosurePenalInterestParamName); + } else { + preClosurePenalInterest = produPreClosureDetail.preClosurePenalInterest(); + } + + if (command.parameterExists(preClosurePenalInterestParamName)) { + preClosurePenalInterestOnTypeId = command.integerValueOfParameterNamed(preClosurePenalInterestOnTypeIdParamName); + } else { + preClosurePenalInterestOnTypeId = produPreClosureDetail.preClosurePenalInterestOnTypeId(); + } + } + } else { + preClosurePenalApplicable = produPreClosureDetail.preClosurePenalApplicable(); + preClosurePenalInterest = produPreClosureDetail.preClosurePenalInterest(); + preClosurePenalInterestOnTypeId = produPreClosureDetail.preClosurePenalInterestOnTypeId(); + } + + preClosurePenalInterestType = preClosurePenalInterestOnTypeId == null ? null : PreClosurePenalInterestOnType + .fromInt(preClosurePenalInterestOnTypeId); + + DepositPreClosureDetail preClosureDetail1 = DepositPreClosureDetail.createFrom(interestFreePeriodApplicable, + interestFreeFromPeriod, interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, + preClosurePenalInterest, preClosurePenalInterestType); + + return preClosureDetail1; + } + + public DepositTermDetail assembleDepositTermDetail(final JsonCommand command) { + + final Integer minDepositTerm = command.integerValueOfParameterNamed(minDepositTermParamName); + final Integer maxDepositTerm = command.integerValueOfParameterNamed(maxDepositTermParamName); + final Integer minDepositTermTypeId = command.integerValueOfParameterNamed(minDepositTermTypeIdParamName); + final SavingsPeriodFrequencyType minDepositTermType = (minDepositTermTypeId == null) ? null : SavingsPeriodFrequencyType + .fromInt(minDepositTermTypeId); + final Integer maxDepositTermTypeId = command.integerValueOfParameterNamed(maxDepositTermTypeIdParamName); + final SavingsPeriodFrequencyType maxDepositTermType = (maxDepositTermTypeId == null) ? null : SavingsPeriodFrequencyType + .fromInt(maxDepositTermTypeId); + final Integer inMultiplesOfDepositTerm = command.integerValueOfParameterNamed(inMultiplesOfDepositTermParamName); + final Integer inMultiplesOfDepositTermTypeId = command.integerValueOfParameterNamed(inMultiplesOfDepositTermTypeIdParamName); + final SavingsPeriodFrequencyType inMultiplesOfDepositTermType = (inMultiplesOfDepositTermTypeId == null) ? null + : SavingsPeriodFrequencyType.fromInt(inMultiplesOfDepositTermTypeId); + + final DepositTermDetail depositTermDetail = DepositTermDetail.createFrom(minDepositTerm, maxDepositTerm, minDepositTermType, + maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType); + + return depositTermDetail; + } + + public DepositTermDetail assembleDepositTermDetail(final JsonCommand command, final DepositTermDetail prodDepositTermDetail) { + + Integer minDepositTerm = null; + Integer maxDepositTerm = null; + Integer minDepositTermTypeId = null; + Integer maxDepositTermTypeId = null; + Integer inMultiplesOfDepositTerm = null; + Integer inMultiplesOfDepositTermTypeId = null; + + if (command.parameterExists(minDepositTermParamName)) { + minDepositTerm = command.integerValueOfParameterNamed(minDepositTermParamName); + } else { + minDepositTerm = prodDepositTermDetail.minDepositTerm(); + } + + if (command.parameterExists(maxDepositTermParamName)) { + maxDepositTerm = command.integerValueOfParameterNamed(maxDepositTermParamName); + } else { + maxDepositTerm = prodDepositTermDetail.maxDepositTerm(); + } + + if (command.parameterExists(minDepositTermTypeIdParamName)) { + minDepositTermTypeId = command.integerValueOfParameterNamed(minDepositTermTypeIdParamName); + } else { + minDepositTermTypeId = prodDepositTermDetail.minDepositTermType(); + } + + if (command.parameterExists(maxDepositTermTypeIdParamName)) { + maxDepositTermTypeId = command.integerValueOfParameterNamed(maxDepositTermTypeIdParamName); + } else { + maxDepositTermTypeId = prodDepositTermDetail.maxDepositTermType(); + } + + final SavingsPeriodFrequencyType minDepositTermType = (minDepositTermTypeId == null) ? null : SavingsPeriodFrequencyType + .fromInt(minDepositTermTypeId); + + final SavingsPeriodFrequencyType maxDepositTermType = (maxDepositTermTypeId == null) ? null : SavingsPeriodFrequencyType + .fromInt(maxDepositTermTypeId); + + if (command.parameterExists(inMultiplesOfDepositTermParamName)) { + inMultiplesOfDepositTerm = command.integerValueOfParameterNamed(inMultiplesOfDepositTermParamName); + } else { + inMultiplesOfDepositTerm = prodDepositTermDetail.inMultiplesOfDepositTerm(); + } + + if (command.parameterExists(preClosurePenalApplicableParamName)) { + inMultiplesOfDepositTermTypeId = command.integerValueOfParameterNamed(inMultiplesOfDepositTermTypeIdParamName); + } else { + inMultiplesOfDepositTermTypeId = prodDepositTermDetail.inMultiplesOfDepositTermType(); + } + + final SavingsPeriodFrequencyType inMultiplesOfDepositTermType = (inMultiplesOfDepositTermTypeId == null) ? null + : SavingsPeriodFrequencyType.fromInt(inMultiplesOfDepositTermTypeId); + + final DepositTermDetail depositTermDetail = DepositTermDetail.createFrom(minDepositTerm, maxDepositTerm, minDepositTermType, + maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType); + + return depositTermDetail; + } + + public DepositRecurringDetail assembleRecurringDetail(final JsonCommand command) { + + RecurringDepositType recurringDepositType = null; + final Integer recurringDepositTypeValue = command.integerValueOfParameterNamed(recurringDepositTypeIdParamName); + if (recurringDepositTypeValue != null) { + recurringDepositType = RecurringDepositType.fromInt(recurringDepositTypeValue); + } + + SavingsPeriodFrequencyType recurringDepositFrequencyType = null; + final Integer recurringDepositFrequencyTypeValue = command.integerValueOfParameterNamed(recurringDepositFrequencyTypeIdParamName); + if (recurringDepositFrequencyTypeValue != null) { + recurringDepositFrequencyType = SavingsPeriodFrequencyType.fromInt(recurringDepositFrequencyTypeValue); + } + + final Integer recurringDepositFrequency = command.integerValueOfParameterNamed(recurringDepositFrequencyParamName); + + final DepositRecurringDetail depositRecurringDetail = DepositRecurringDetail.createFrom(recurringDepositType, + recurringDepositFrequency, recurringDepositFrequencyType); + + return depositRecurringDetail; + } + + public Set assembleListOfSavingsProductCharges(final JsonCommand command, final String savingsProductCurrencyCode) { + + final Set charges = new HashSet(); + + if (command.parameterExists(chargesParamName)) { + final JsonArray chargesArray = command.arrayOfParameterNamed(chargesParamName); + if (chargesArray != null) { + for (int i = 0; i < chargesArray.size(); i++) { + + final JsonObject jsonObject = chargesArray.get(i).getAsJsonObject(); + if (jsonObject.has(idParamName)) { + final Long id = jsonObject.get(idParamName).getAsLong(); + + final Charge charge = this.chargeRepository.findOneWithNotFoundDetection(id); + + if (!charge.isSavingsCharge()) { + final String errorMessage = "Charge with identifier " + charge.getId() + + " cannot be applied to Savings product."; + throw new ChargeCannotBeAppliedToException("savings.product", errorMessage, charge.getId()); + } + + if (!savingsProductCurrencyCode.equals(charge.getCurrencyCode())) { + final String errorMessage = "Charge and Savings Product must have the same currency."; + throw new InvalidCurrencyException("charge", "attach.to.savings.product", errorMessage); + } + charges.add(charge); + } + } + } + } + + return charges; + } + + private Set assembleListOfCharts(JsonCommand command, String currencyCode) { + final Set charts = new HashSet(); + + if (command.parameterExists(chartsParamName)) { + final JsonArray chartsArray = command.arrayOfParameterNamed(chartsParamName); + if (chartsArray != null) { + for (int i = 0; i < chartsArray.size(); i++) { + final JsonObject interstRateChartElement = chartsArray.get(i).getAsJsonObject(); + InterestRateChart chart = this.chartAssembler.assembleFrom(interstRateChartElement, currencyCode); + charts.add(chart); + } + } + } + return charts; + } + + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositProductRecurringDetail.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositProductRecurringDetail.java new file mode 100644 index 000000000..064214607 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositProductRecurringDetail.java @@ -0,0 +1,61 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; +import javax.persistence.Table; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "m_deposit_product_recurring_detail") +public class DepositProductRecurringDetail extends AbstractPersistable { + + @Embedded + private DepositRecurringDetail recurringDetail; + + @OneToOne + @JoinColumn(name = "savings_product_id", nullable = false) + private RecurringDepositProduct product; + + protected DepositProductRecurringDetail() { + super(); + } + + public static DepositProductRecurringDetail createNew(DepositRecurringDetail recurringDetail, SavingsProduct product) { + + return new DepositProductRecurringDetail(recurringDetail, product); + } + + private DepositProductRecurringDetail(DepositRecurringDetail recurringDetail, SavingsProduct product) { + this.recurringDetail = recurringDetail; + this.product = (RecurringDepositProduct) product; + } + + public Map update(final JsonCommand command, final DataValidatorBuilder baseDataValidator) { + final Map actualChanges = new LinkedHashMap(10); + if (this.recurringDetail != null) { + actualChanges.putAll(this.recurringDetail.update(command, baseDataValidator)); + } + return actualChanges; + } + + public DepositRecurringDetail recurringDetail() { + return this.recurringDetail; + } + + public void updateProductReference(final SavingsProduct product) { + this.product = (RecurringDepositProduct) product; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositProductTermAndPreClosure.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositProductTermAndPreClosure.java new file mode 100644 index 000000000..5e7b6f4e5 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositProductTermAndPreClosure.java @@ -0,0 +1,75 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; +import javax.persistence.Table; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "m_deposit_product_term_and_preclosure") +public class DepositProductTermAndPreClosure extends AbstractPersistable { + + @Embedded + private DepositPreClosureDetail preClosureDetail; + + @Embedded + protected DepositTermDetail depositTermDetail; + + @OneToOne + @JoinColumn(name = "savings_product_id", nullable = false) + private FixedDepositProduct product; + + protected DepositProductTermAndPreClosure() { + super(); + } + + public static DepositProductTermAndPreClosure createNew(DepositPreClosureDetail preClosureDetail, DepositTermDetail depositTermDetail, + SavingsProduct product) { + + return new DepositProductTermAndPreClosure(preClosureDetail, depositTermDetail, product); + } + + private DepositProductTermAndPreClosure(DepositPreClosureDetail preClosureDetail, DepositTermDetail depositTermDetail, + SavingsProduct product) { + this.preClosureDetail = preClosureDetail; + this.depositTermDetail = depositTermDetail; + this.product = (FixedDepositProduct)product; + } + + public Map update(final JsonCommand command, final DataValidatorBuilder baseDataValidator) { + final Map actualChanges = new LinkedHashMap(10); + if (this.preClosureDetail != null) { + actualChanges.putAll(this.preClosureDetail.update(command, baseDataValidator)); + } + + if (this.depositTermDetail != null) { + actualChanges.putAll(this.depositTermDetail.update(command, baseDataValidator)); + } + return actualChanges; + } + + public DepositPreClosureDetail depositPreClosureDetail() { + return this.preClosureDetail; + } + + public DepositTermDetail depositTermDetail() { + return this.depositTermDetail; + } + + public void updateProductReference(final SavingsProduct product){ + this.product = (FixedDepositProduct)product; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositRecurringDetail.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositRecurringDetail.java new file mode 100644 index 000000000..f0f92db53 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositRecurringDetail.java @@ -0,0 +1,105 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositFrequencyParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositFrequencyTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositTypeIdParamName; + +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.persistence.Column; +import javax.persistence.Embeddable; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.portfolio.savings.RecurringDepositType; +import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; +import org.mifosplatform.portfolio.savings.service.SavingsEnumerations; + +/** + * DepositRecurringDetail encapsulates all the details of a + * {@link RecurringDepositProduct} that are also used and persisted by a + * {@link RecurringDepositAccount}. + */ +@Embeddable +public class DepositRecurringDetail { + + @Column(name = "recurring_deposit_type_enum", nullable = true) + private Integer recurringDepositType; + + @Column(name = "recurring_deposit_frequency", nullable = true) + private Integer recurringDepositFrequency; + + @Column(name = "recurring_deposit_frequency_type_enum", nullable = true) + private Integer recurringDepositFrequencyType; + + public static DepositRecurringDetail createFrom(final RecurringDepositType recurringDepositType, + final Integer recurringDepositFrequency, final SavingsPeriodFrequencyType recurringDepositFrequencyType) { + + return new DepositRecurringDetail(recurringDepositType, recurringDepositFrequency, recurringDepositFrequencyType); + } + + protected DepositRecurringDetail() { + // + } + + private DepositRecurringDetail(final RecurringDepositType recurringDepositType, final Integer recurringDepositFrequency, + final SavingsPeriodFrequencyType recurringDepositFrequencyType) { + this.recurringDepositFrequency = recurringDepositFrequency; + this.recurringDepositFrequencyType = recurringDepositFrequencyType.getValue(); + this.recurringDepositType = recurringDepositType.getValue(); + } + + public Map update(final JsonCommand command, @SuppressWarnings("unused") final DataValidatorBuilder baseDataValidator) { + final Map actualChanges = new LinkedHashMap(10); + + if (command.isChangeInIntegerParameterNamed(recurringDepositFrequencyParamName, this.recurringDepositFrequency)) { + final Integer newValue = command.integerValueOfParameterNamed(recurringDepositFrequencyParamName); + actualChanges.put(recurringDepositFrequencyParamName, newValue); + this.recurringDepositFrequency = newValue; + } + + if (command.isChangeInIntegerParameterNamed(recurringDepositFrequencyTypeIdParamName, this.recurringDepositFrequencyType)) { + final Integer newValue = command.integerValueOfParameterNamed(recurringDepositFrequencyTypeIdParamName); + actualChanges.put(recurringDepositFrequencyTypeIdParamName, SavingsEnumerations.recurringDepositFrequencyType(newValue)); + this.recurringDepositFrequencyType = newValue; + } + + if (command.isChangeInIntegerParameterNamed(recurringDepositTypeIdParamName, this.recurringDepositType)) { + final Integer newValue = command.integerValueOfParameterNamed(recurringDepositTypeIdParamName); + actualChanges.put(recurringDepositTypeIdParamName, SavingsEnumerations.recurringDepositType(newValue)); + this.recurringDepositType = newValue; + } + + return actualChanges; + } + + public Integer recurringDepositType() { + return this.recurringDepositType; + } + + public Integer recurringDepositFrequency() { + return this.recurringDepositFrequency; + } + + public Integer recurringDepositFrequencyTypeId() { + return this.recurringDepositFrequencyType; + } + + public SavingsPeriodFrequencyType recurringDepositFrequencyType() { + return SavingsPeriodFrequencyType.fromInt(this.recurringDepositFrequencyType); + } + + public DepositRecurringDetail copy() { + final RecurringDepositType recurringDepositType = RecurringDepositType.fromInt(this.recurringDepositType); + final Integer recurringDepositFrequency = this.recurringDepositFrequency; + final SavingsPeriodFrequencyType recurringDepositFrequencyType = SavingsPeriodFrequencyType + .fromInt(this.recurringDepositFrequencyType); + return DepositRecurringDetail.createFrom(recurringDepositType, recurringDepositFrequency, recurringDepositFrequencyType); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositTermDetail.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositTermDetail.java new file mode 100644 index 000000000..b8a6368ed --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositTermDetail.java @@ -0,0 +1,246 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.minDepositTermTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.maxDepositTermTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.inMultiplesOfDepositTermTypeIdParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.inMultiplesOfDepositTermParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.maxDepositTermParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.minDepositTermParamName; + +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.persistence.Column; +import javax.persistence.Embeddable; + +import org.joda.time.DateTimeConstants; +import org.joda.time.Days; +import org.joda.time.LocalDate; +import org.joda.time.Months; +import org.joda.time.Weeks; +import org.joda.time.Years; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; +import org.mifosplatform.portfolio.savings.service.SavingsEnumerations; + +/** + * DepositTermDetail encapsulates all the details of a + * {@link FixedDepositProduct} that are also used and persisted by a + * {@link FixedDepositAccount}. + */ +@Embeddable +public class DepositTermDetail { + + @Column(name = "min_deposit_term", nullable = true) + private Integer minDepositTerm; + + @Column(name = "max_deposit_term", nullable = true) + private Integer maxDepositTerm; + + @Column(name = "min_deposit_term_type_enum", nullable = true) + private Integer minDepositTermType; + + @Column(name = "max_deposit_term_type_enum", nullable = true) + private Integer maxDepositTermType; + + @Column(name = "in_multiples_of_deposit_term", nullable = true) + private Integer inMultiplesOfDepositTerm; + + @Column(name = "in_multiples_of_deposit_term_type_enum", nullable = true) + private Integer inMultiplesOfDepositTermType; + + public static DepositTermDetail createFrom(final Integer minDepositTerm, final Integer maxDepositTerm, + final SavingsPeriodFrequencyType minDepositTermType, final SavingsPeriodFrequencyType maxDepositTermType, + final Integer inMultiplesOfDepositTerm, final SavingsPeriodFrequencyType inMultiplesOfDepositTermType) { + + return new DepositTermDetail(minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, + inMultiplesOfDepositTermType); + } + + protected DepositTermDetail() { + // + } + + private DepositTermDetail(final Integer minDepositTerm, final Integer maxDepositTerm, + final SavingsPeriodFrequencyType minDepositTermType, final SavingsPeriodFrequencyType maxDepositTermType, + final Integer inMultiplesOfDepositTerm, final SavingsPeriodFrequencyType inMultiplesOfDepositTermType) { + this.minDepositTerm = minDepositTerm; + this.maxDepositTerm = maxDepositTerm; + this.minDepositTermType = (minDepositTermType == null) ? null : minDepositTermType.getValue(); + this.maxDepositTermType = (maxDepositTermType == null) ? null : maxDepositTermType.getValue(); + this.inMultiplesOfDepositTerm = inMultiplesOfDepositTerm; + this.inMultiplesOfDepositTermType = (inMultiplesOfDepositTermType == null) ? null : inMultiplesOfDepositTermType.getValue(); + } + + public Map update(final JsonCommand command, final DataValidatorBuilder baseDataValidator) { + final Map actualChanges = new LinkedHashMap(10); + + if (command.isChangeInIntegerParameterNamed(minDepositTermParamName, this.minDepositTerm)) { + final Integer newValue = command.integerValueOfParameterNamed(minDepositTermParamName); + actualChanges.put(minDepositTermParamName, newValue); + this.minDepositTerm = newValue; + } + + if (command.isChangeInIntegerParameterNamed(maxDepositTermParamName, this.maxDepositTerm)) { + final Integer newValue = command.integerValueOfParameterNamed(maxDepositTermParamName); + actualChanges.put(maxDepositTermParamName, newValue); + this.maxDepositTerm = newValue; + } + + if (command.isChangeInIntegerParameterNamed(minDepositTermTypeIdParamName, this.minDepositTermType)) { + final Integer newValue = command.integerValueOfParameterNamed(minDepositTermTypeIdParamName); + actualChanges.put(minDepositTermTypeIdParamName, SavingsEnumerations.depositTermFrequencyType(newValue)); + this.minDepositTermType = newValue; + } + + if (command.isChangeInIntegerParameterNamed(maxDepositTermTypeIdParamName, this.maxDepositTermType)) { + final Integer newValue = command.integerValueOfParameterNamed(maxDepositTermTypeIdParamName); + actualChanges.put(maxDepositTermTypeIdParamName, SavingsEnumerations.depositTermFrequencyType(newValue)); + this.maxDepositTermType = newValue; + } + + if (command.isChangeInIntegerParameterNamed(inMultiplesOfDepositTermParamName, this.inMultiplesOfDepositTerm)) { + final Integer newValue = command.integerValueOfParameterNamed(inMultiplesOfDepositTermParamName); + actualChanges.put(inMultiplesOfDepositTermParamName, newValue); + this.inMultiplesOfDepositTerm = newValue; + } + + if (command.isChangeInIntegerParameterNamed(inMultiplesOfDepositTermTypeIdParamName, this.inMultiplesOfDepositTermType)) { + final Integer newValue = command.integerValueOfParameterNamed(inMultiplesOfDepositTermTypeIdParamName); + actualChanges.put(inMultiplesOfDepositTermTypeIdParamName, SavingsEnumerations.inMultiplesOfDepositTermFrequencyType(newValue)); + this.inMultiplesOfDepositTermType = newValue; + } + + if (isMinDepositTermGreaterThanMaxDepositTerm()) { + baseDataValidator.parameter(minDepositTermParamName).value(this.minDepositTerm) + .failWithCode(".greater.than.maxDepositTerm", this.minDepositTerm, this.maxDepositTerm); + } + + return actualChanges; + } + + public boolean isMinDepositTermGreaterThanMaxDepositTerm() { + if (this.minDepositTerm != null && this.maxDepositTerm != null) { + final Integer minDepositInDays = this.convertToSafeDays(minDepositTerm, SavingsPeriodFrequencyType.fromInt(minDepositTermType)); + final Integer maxDepositInDays = this.convertToSafeDays(maxDepositTerm, SavingsPeriodFrequencyType.fromInt(maxDepositTermType)); + if (minDepositInDays.compareTo(maxDepositInDays) > 0) { return true; } + } + return false; + } + + public Integer minDepositTerm() { + return this.minDepositTerm; + } + + public Integer maxDepositTerm() { + return this.maxDepositTerm; + } + + public Integer minDepositTermType() { + return this.minDepositTermType; + } + + public Integer maxDepositTermType() { + return this.maxDepositTermType; + } + + public Integer inMultiplesOfDepositTerm() { + return this.inMultiplesOfDepositTerm; + } + + public Integer inMultiplesOfDepositTermType() { + return this.inMultiplesOfDepositTermType; + } + + public boolean isDepositBetweenMinAndMax(LocalDate depositStartDate, LocalDate depositEndDate) { + return isEqualOrGreaterThanMin(depositStartDate, depositEndDate) && isEqualOrLessThanMax(depositStartDate, depositEndDate); + } + + public boolean isValidInMultiplesOfPeriod(final Integer depositPeriod, final SavingsPeriodFrequencyType depositPeriodFrequencyType) { + + final Integer depositPeriodInDays = this.convertToSafeDays(depositPeriod, depositPeriodFrequencyType); + final Integer inMultiplesOfInDays = this.convertToSafeDays(this.inMultiplesOfDepositTerm(), + SavingsPeriodFrequencyType.fromInt(this.inMultiplesOfDepositTermType())); + final Integer minDepositInDays = this.convertToSafeDays(minDepositTerm, SavingsPeriodFrequencyType.fromInt(minDepositTermType)); + + return ((depositPeriodInDays - minDepositInDays) % inMultiplesOfInDays == 0); + } + + private boolean isEqualOrGreaterThanMin(LocalDate depositStartDate, LocalDate depositEndDate) { + final SavingsPeriodFrequencyType periodFrequencyType = SavingsPeriodFrequencyType.fromInt(this.minDepositTermType()); + final Integer depositPeriod = depositPeriod(depositStartDate, depositEndDate, periodFrequencyType); + return depositPeriod.compareTo(minDepositTerm()) >= 0; + } + + private boolean isEqualOrLessThanMax(LocalDate depositStartDate, LocalDate depositEndDate) { + final SavingsPeriodFrequencyType periodFrequencyType = SavingsPeriodFrequencyType.fromInt(this.maxDepositTermType()); + final Integer depositPeriod = depositPeriod(depositStartDate, depositEndDate, periodFrequencyType); + return depositPeriod.compareTo(maxDepositTerm()) <= 0; + } + + public Integer depositPeriod(final LocalDate periodStartDate, final LocalDate periodEndDate, + final SavingsPeriodFrequencyType periodFrequencyType) { + Integer actualDepositPeriod = 0; + + switch (periodFrequencyType) { + case DAYS: + actualDepositPeriod = Days.daysBetween(periodStartDate, periodEndDate).getDays(); + break; + case WEEKS: + actualDepositPeriod = Weeks.weeksBetween(periodStartDate, periodEndDate).getWeeks(); + break; + case MONTHS: + actualDepositPeriod = Months.monthsBetween(periodStartDate, periodEndDate).getMonths(); + break; + case YEARS: + actualDepositPeriod = Years.yearsBetween(periodStartDate, periodEndDate).getYears(); + break; + case INVALID: + actualDepositPeriod = 0;// default value + break; + } + return actualDepositPeriod; + } + + private Integer convertToSafeDays(final Integer period, final SavingsPeriodFrequencyType periodFrequencyType) { + Integer toDays = 0; + switch (periodFrequencyType) { + case DAYS: + toDays = period; + break; + case WEEKS: + toDays = period * DateTimeConstants.DAYS_PER_WEEK; + break; + case MONTHS: + toDays = period * 30;// converting to stard 30 days + break; + case YEARS: + toDays = period * 365; + break; + case INVALID: + toDays = 0;// default value + break; + } + return toDays; + } + + public DepositTermDetail copy() { + + final Integer minDepositTerm = this.minDepositTerm; + final Integer maxDepositTerm = this.maxDepositTerm; + final SavingsPeriodFrequencyType minDepositTermType = SavingsPeriodFrequencyType.fromInt(this.minDepositTermType); + final SavingsPeriodFrequencyType maxDepositTermType = SavingsPeriodFrequencyType.fromInt(this.maxDepositTermType); + final Integer inMultiplesOfDepositTerm = this.inMultiplesOfDepositTerm; + final SavingsPeriodFrequencyType inMultiplesOfDepositTermType = SavingsPeriodFrequencyType.fromInt(this + .inMultiplesOfDepositTermType()); + + return DepositTermDetail.createFrom(minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, + inMultiplesOfDepositTerm, inMultiplesOfDepositTermType); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/FixedDepositAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/FixedDepositAccount.java new file mode 100644 index 000000000..271ab1a54 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/FixedDepositAccount.java @@ -0,0 +1,697 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositPeriodParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.maxDepositTermParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.onAccountClosureIdParamName; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import javax.persistence.CascadeType; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.OneToOne; +import javax.persistence.Transient; + +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.domain.LocalDateInterval; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.core.service.DateUtils; +import org.mifosplatform.organisation.monetary.domain.Money; +import org.mifosplatform.organisation.staff.domain.Staff; +import org.mifosplatform.portfolio.accountdetails.domain.AccountType; +import org.mifosplatform.portfolio.client.domain.Client; +import org.mifosplatform.portfolio.group.domain.Group; +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChart; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartAssembler; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; +import org.mifosplatform.portfolio.savings.DepositAccountOnClosureType; +import org.mifosplatform.portfolio.savings.DepositsApiConstants; +import org.mifosplatform.portfolio.savings.PreClosurePenalInterestOnType; +import org.mifosplatform.portfolio.savings.SavingsApiConstants; +import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; +import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; +import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType; +import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionDTO; +import org.mifosplatform.portfolio.savings.domain.interest.PostingPeriod; +import org.mifosplatform.portfolio.savings.service.SavingsEnumerations; +import org.mifosplatform.useradministration.domain.AppUser; + +@Entity +@DiscriminatorValue("200") +public class FixedDepositAccount extends SavingsAccount { + + @OneToOne(mappedBy = "account", cascade = CascadeType.ALL) + private DepositAccountTermAndPreClosure accountTermAndPreClosure; + + @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "account") + protected DepositAccountInterestRateChart chart; + + @Transient + protected InterestRateChartAssembler chartAssembler; + + protected FixedDepositAccount() { + // + } + + public static FixedDepositAccount createNewApplicationForSubmittal(final Client client, final Group group, + final SavingsProduct product, final Staff fieldOfficer, final String accountNo, final String externalId, + final AccountType accountType, final LocalDate submittedOnDate, final AppUser submittedBy, final BigDecimal interestRate, + final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, + final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, + final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, + final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, + final boolean withdrawalFeeApplicableForTransfer, final Set savingsAccountCharges, + final DepositAccountTermAndPreClosure accountTermAndPreClosure, final DepositAccountInterestRateChart chart) { + + final SavingsAccountStatusType status = SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL; + final boolean allowOverdraft = false; + final BigDecimal overdraftLimit = new BigDecimal(0); + FixedDepositAccount account = new FixedDepositAccount(client, group, product, fieldOfficer, accountNo, externalId, status, + accountType, submittedOnDate, submittedBy, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, + interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, + lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, savingsAccountCharges, accountTermAndPreClosure, chart, + allowOverdraft, overdraftLimit); + + return account; + } + + private FixedDepositAccount(final Client client, final Group group, final SavingsProduct product, final Staff fieldOfficer, + final String accountNo, final String externalId, final SavingsAccountStatusType status, final AccountType accountType, + final LocalDate submittedOnDate, final AppUser submittedBy, final BigDecimal nominalAnnualInterestRate, + final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, + final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, + final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, + final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, + final boolean withdrawalFeeApplicableForTransfer, final Set savingsAccountCharges, + final DepositAccountTermAndPreClosure accountTermAndPreClosure, DepositAccountInterestRateChart chart, + final boolean allowOverdraft, final BigDecimal overdraftLimit) { + + super(client, group, product, fieldOfficer, accountNo, externalId, status, accountType, submittedOnDate, submittedBy, + nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, + interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, + withdrawalFeeApplicableForTransfer, savingsAccountCharges, allowOverdraft, overdraftLimit); + + this.accountTermAndPreClosure = accountTermAndPreClosure; + this.chart = chart; + if (this.chart != null) { + this.chart.updateDepositAccountReference(this); + } + } + + @Override + public void modifyApplication(final JsonCommand command, final Map actualChanges) { + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.modifyApplicationAction); + super.modifyApplication(command, actualChanges, baseDataValidator); + + accountTermAndPreClosure.update(command, baseDataValidator); + validateDomainRules(baseDataValidator); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + @Override + protected BigDecimal getEffectiveInterestRateAsFraction(final MathContext mc, final LocalDate interestPostingUpToDate) { + + final boolean isPreMatureClosure = SavingsAccountStatusType.fromInt(this.status).isPreMatureClosure(); + boolean applyPreMaturePenalty = false; + BigDecimal penalInterest = BigDecimal.ZERO; + LocalDate depositCloseDate = calculateMaturityDate(); + if (isPreMatureClosure) { + if (this.accountTermAndPreClosure.isPreClosurePenalApplicable()) { + applyPreMaturePenalty = true; + penalInterest = this.accountTermAndPreClosure.depositPreClosureDetail().preClosurePenalInterest(); + final PreClosurePenalInterestOnType preClosurePenalInterestOnType = this.accountTermAndPreClosure.depositPreClosureDetail() + .preClosurePenalInterestOnType(); + if (preClosurePenalInterestOnType.isWholeTerm()) { + depositCloseDate = interestCalculatedUpto(); + } else if (preClosurePenalInterestOnType.isTillPrematureWithdrawal()) { + depositCloseDate = interestPostingUpToDate; + } + } + } + + final BigDecimal depositAmount = accountTermAndPreClosure.depositAmount(); + + BigDecimal applicableInterestRate = this.chart.getApplicableInterestRate(depositAmount, depositStartDate(), depositCloseDate); + + if (applyPreMaturePenalty) { + applicableInterestRate = applicableInterestRate.subtract(penalInterest); + applicableInterestRate = applicableInterestRate.compareTo(BigDecimal.ZERO) == -1 ? BigDecimal.ZERO : applicableInterestRate; + } + + this.nominalAnnualInterestRate = applicableInterestRate; + + return applicableInterestRate.divide(BigDecimal.valueOf(100l), mc); + } + + public void updateMaturityDateAndAmountBeforeAccountActivation(final MathContext mc) { + List allTransactions = new ArrayList(); + final Money transactionAmountMoney = Money.of(getCurrency(), this.accountTermAndPreClosure.depositAmount()); + final SavingsAccountTransaction transaction = SavingsAccountTransaction.deposit(null, office(), null, + this.accountSubmittedOrActivationDate(), transactionAmountMoney, new Date()); + transaction.updateRunningBalance(transactionAmountMoney); + transaction.updateCumulativeBalanceAndDates(this.getCurrency(), interestCalculatedUpto()); + allTransactions.add(transaction); + updateMaturityDateAndAmount(mc, allTransactions); + } + + public void updateMaturityDateAndAmount(final MathContext mc, final List transactions) { + final LocalDate maturityDate = calculateMaturityDate(); + final LocalDate interestCalculationUpto = maturityDate.minusDays(1); + final List postingPeriods = calculateInterestPayable(mc, interestCalculationUpto, transactions); + Money totalInterestPayable = Money.zero(getCurrency()); + for (PostingPeriod postingPeriod : postingPeriods) { + totalInterestPayable = totalInterestPayable.plus(postingPeriod.getInterestEarned()); + } + final Money depositAmount = Money.of(getCurrency(), this.accountTermAndPreClosure.depositAmount()); + final Money maturityAmount = depositAmount.plus(totalInterestPayable); + + this.accountTermAndPreClosure.updateMaturityDetails(maturityAmount.getAmount(), maturityDate); + } + + public void updateMaturityStatus() { + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.updateMaturityDetailsAction); + + final SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); + if (!SavingsAccountStatusType.ACTIVE.hasStateOf(currentStatus)) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("not.in.active.state"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + final LocalDate todayDate = DateUtils.getLocalDateOfTenant(); + if (!this.maturityDate().isAfter(todayDate)) { + // update account status + this.status = SavingsAccountStatusType.MATURED.getValue(); + postMaturityInterest(maturityDate()); + } + } + + public LocalDate calculateMaturityDate() { + + final LocalDate startDate = accountSubmittedOrActivationDate(); + LocalDate maturityDate = null; + final Integer depositPeriod = this.accountTermAndPreClosure.depositPeriod(); + switch (this.accountTermAndPreClosure.depositPeriodFrequencyType()) { + case DAYS: + maturityDate = startDate.plusDays(depositPeriod); + break; + case WEEKS: + maturityDate = startDate.plusWeeks(depositPeriod); + break; + case MONTHS: + maturityDate = startDate.plusMonths(depositPeriod); + break; + case YEARS: + maturityDate = startDate.plusYears(depositPeriod); + break; + case INVALID: + break; + } + + return maturityDate; + } + + private List calculateInterestPayable(final MathContext mc, final LocalDate maturityDate, + final List transactions) { + + // 1. default to calculate interest based on entire history OR + // 2. determine latest 'posting period' and find interest credited to + // that period + + // A generate list of EndOfDayBalances (not including interest postings) + final SavingsPostingInterestPeriodType postingPeriodType = SavingsPostingInterestPeriodType.fromInt(this.interestPostingPeriodType); + + final SavingsCompoundingInterestPeriodType compoundingPeriodType = SavingsCompoundingInterestPeriodType + .fromInt(this.interestCompoundingPeriodType); + + final SavingsInterestCalculationDaysInYearType daysInYearType = SavingsInterestCalculationDaysInYearType + .fromInt(this.interestCalculationDaysInYearType); + + final List postingPeriodIntervals = this.savingsHelper.determineInterestPostingPeriods( + accountSubmittedOrActivationDate(), maturityDate, postingPeriodType); + + final List allPostingPeriods = new ArrayList(); + + Money periodStartingBalance = Money.of(getCurrency(), this.accountTermAndPreClosure.depositAmount()); + + final SavingsInterestCalculationType interestCalculationType = SavingsInterestCalculationType.fromInt(this.interestCalculationType); + final BigDecimal interestRateAsFraction = getEffectiveInterestRateAsFraction(mc, maturityDate); + for (final LocalDateInterval periodInterval : postingPeriodIntervals) { + + final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance, transactions, + this.currency, compoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYearType.getValue(), + maturityDate); + + periodStartingBalance = postingPeriod.closingBalance(); + + allPostingPeriods.add(postingPeriod); + } + + this.summary.updateFromInterestPeriodSummaries(this.currency, allPostingPeriods); + this.savingsHelper.calculateInterestForAllPostingPeriods(this.currency, allPostingPeriods); + return allPostingPeriods; + } + + public SavingsAccountTransaction prematureClosure(final AppUser currentUser, final JsonCommand command, + final LocalDate tenantsTodayDate, final PaymentDetail paymentDetail, final Map actualChanges) { + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME + DepositsApiConstants.preMatureCloseAction); + + final SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); + if (!SavingsAccountStatusType.ACTIVE.hasStateOf(currentStatus)) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("not.in.active.state"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); + final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName); + + if (closedDate.isBefore(getActivationLocalDate())) { + baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) + .failWithCode("must.be.after.activation.date"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + if (closedDate.isAfter(maturityDate())) { + baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) + .failWithCode("must.be.before.maturity.date"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + if (closedDate.isAfter(tenantsTodayDate)) { + baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) + .failWithCode("cannot.be.a.future.date"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + final List savingsAccountTransactions = retreiveListOfTransactions(); + if (savingsAccountTransactions.size() > 0) { + final SavingsAccountTransaction accountTransaction = savingsAccountTransactions.get(savingsAccountTransactions.size() - 1); + if (accountTransaction.isAfter(closedDate)) { + baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) + .failWithCode("must.be.after.last.transaction.date"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + } + + validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_CLOSE_ACCOUNT, closedDate); + this.status = SavingsAccountStatusType.PRE_MATURE_CLOSURE.getValue(); + + final Integer onAccountClosureId = command.integerValueOfParameterNamed(onAccountClosureIdParamName); + final DepositAccountOnClosureType onClosureType = DepositAccountOnClosureType.fromInt(onAccountClosureId); + this.accountTermAndPreClosure.updateOnAccountClosureStatus(onClosureType); + + postPreMaturityInterest(closedDate); + + // withdraw deposit amount before closing the account + final Money transactionAmountMoney = Money.of(this.currency, this.getAccountBalance()); + final SavingsAccountTransaction withdraw = SavingsAccountTransaction.withdrawal(this, office(), paymentDetail, closedDate, + transactionAmountMoney, new Date()); + this.transactions.add(withdraw); + + actualChanges.put(SavingsApiConstants.statusParamName, SavingsEnumerations.status(this.status)); + actualChanges.put(SavingsApiConstants.localeParamName, command.locale()); + actualChanges.put(SavingsApiConstants.dateFormatParamName, command.dateFormat()); + actualChanges.put(SavingsApiConstants.closedOnDateParamName, closedDate.toString(fmt)); + + this.rejectedOnDate = null; + this.rejectedBy = null; + this.withdrawnOnDate = null; + this.withdrawnBy = null; + this.closedOnDate = closedDate.toDate(); + this.closedBy = currentUser; + + return withdraw; + } + + @Override + protected void processAccountUponActivation(final DateTimeFormatter fmt) { + final Money depositAmount = Money.of(this.currency, this.accountTermAndPreClosure.depositAmount()); + if (depositAmount.isGreaterThanZero()) { + + final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, getActivationLocalDate(), + depositAmount.getAmount(), null, new Date()); + deposit(transactionDTO); + final LocalDate interestPostingUpToDate = interestPostingUpToDate(DateUtils.getLocalDateOfTenant()); + // update existing transactions so derived balance fields are + // correct. + recalculateDailyBalances(Money.zero(this.currency), interestPostingUpToDate); + } + } + + public SavingsAccountTransaction close(final AppUser currentUser, final JsonCommand command, final LocalDate tenantsTodayDate, + final PaymentDetail paymentDetail, final Map actualChanges) { + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.closeAction); + + final SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); + if (!SavingsAccountStatusType.MATURED.hasStateOf(currentStatus)) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("not.in.matured.state"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); + final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName); + + if (closedDate.isBefore(getActivationLocalDate())) { + baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) + .failWithCode("must.be.after.activation.date"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + if (closedDate.isBefore(maturityDate())) { + baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) + .failWithCode("must.be.after.account.maturity.date"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + if (closedDate.isAfter(tenantsTodayDate)) { + baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) + .failWithCode("cannot.be.a.future.date"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + final List savingsAccountTransactions = retreiveListOfTransactions(); + if (savingsAccountTransactions.size() > 0) { + final SavingsAccountTransaction accountTransaction = savingsAccountTransactions.get(savingsAccountTransactions.size() - 1); + if (accountTransaction.isAfter(closedDate)) { + baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) + .failWithCode("must.be.after.last.transaction.date"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + } + + validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_CLOSE_ACCOUNT, closedDate); + this.status = SavingsAccountStatusType.CLOSED.getValue(); + + final Integer onAccountClosureId = command.integerValueOfParameterNamed(onAccountClosureIdParamName); + final DepositAccountOnClosureType onClosureType = DepositAccountOnClosureType.fromInt(onAccountClosureId); + this.accountTermAndPreClosure.updateOnAccountClosureStatus(onClosureType); + + postMaturityInterest(maturityDate()); + + // withdraw deposit amount before closing the account + final Money transactionAmountMoney = Money.of(this.currency, this.getAccountBalance()); + final SavingsAccountTransaction withdraw = SavingsAccountTransaction.withdrawal(this, office(), paymentDetail, closedDate, + transactionAmountMoney, new Date()); + this.transactions.add(withdraw); + + actualChanges.put(SavingsApiConstants.statusParamName, SavingsEnumerations.status(this.status)); + actualChanges.put(SavingsApiConstants.localeParamName, command.locale()); + actualChanges.put(SavingsApiConstants.dateFormatParamName, command.dateFormat()); + actualChanges.put(SavingsApiConstants.closedOnDateParamName, closedDate.toString(fmt)); + + this.rejectedOnDate = null; + this.rejectedBy = null; + this.withdrawnOnDate = null; + this.withdrawnBy = null; + this.closedOnDate = closedDate.toDate(); + this.closedBy = currentUser; + + return withdraw; + } + + public void postMaturityInterest(final LocalDate interestPostingUpToDate) { + + final MathContext mc = MathContext.DECIMAL64; + + final List postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate); + + Money interestPostedToDate = Money.zero(this.currency); + + boolean recalucateDailyBalanceDetails = false; + + for (final PostingPeriod interestPostingPeriod : postingPeriods) { + + LocalDate interestPostingTransactionDate = interestPostingPeriod.dateOfPostingTransaction(); + + interestPostingTransactionDate = interestPostingTransactionDate.isAfter(interestPostingUpToDate) ? interestPostingUpToDate + : interestPostingTransactionDate; + + final Money interestEarnedToBePostedForPeriod = interestPostingPeriod.getInterestEarned(); + + interestPostedToDate = interestPostedToDate.plus(interestEarnedToBePostedForPeriod); + + final SavingsAccountTransaction postingTransaction = findInterestPostingTransactionFor(interestPostingTransactionDate); + if (postingTransaction == null) { + final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), + interestPostingTransactionDate, interestEarnedToBePostedForPeriod); + this.transactions.add(newPostingTransaction); + recalucateDailyBalanceDetails = true; + } else { + final boolean correctionRequired = postingTransaction.hasNotAmount(interestEarnedToBePostedForPeriod); + if (correctionRequired) { + postingTransaction.reverse(); + final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), + interestPostingTransactionDate, interestEarnedToBePostedForPeriod); + this.transactions.add(newPostingTransaction); + recalucateDailyBalanceDetails = true; + } + } + } + + if (recalucateDailyBalanceDetails) { + // update existing transactions so derived balance fields are + // correct. + recalculateDailyBalances(Money.zero(this.currency), interestPostingUpToDate); + } + + this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); + } + + public void postPreMaturityInterest(final LocalDate accountCloseDate) { + + Money interestPostedToDate = totalInterestPosted(); + final Money interestOnMaturity = calculatePreMatureInterest(accountCloseDate, retreiveOrderedNonInterestPostingTransactions()); + boolean recalucateDailyBalance = false; + + // post remaining interest + final Money remainigInterestToBePosted = interestOnMaturity.minus(interestPostedToDate); + if (!remainigInterestToBePosted.isZero()) { + final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), + accountCloseDate, remainigInterestToBePosted); + this.transactions.add(newPostingTransaction); + recalucateDailyBalance = true; + } + + if (recalucateDailyBalance) { + // update existing transactions so derived balance fields are + // correct. + recalculateDailyBalances(Money.zero(this.currency), accountCloseDate); + } + this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); + this.accountTermAndPreClosure.updateMaturityDetails(this.getAccountBalance(), accountCloseDate); + + } + + public BigDecimal calculatePreMatureAmount(final LocalDate preMatureDate) { + + final Money interestPostedToDate = totalInterestPosted().copy(); + + final Money interestEarnedTillDate = calculatePreMatureInterest(preMatureDate, retreiveOrderedNonInterestPostingTransactions()); + + final Money accountBalance = Money.of(getCurrency(), getAccountBalance()); + final Money maturityAmount = accountBalance.minus(interestPostedToDate).plus(interestEarnedTillDate); + + return maturityAmount.getAmount(); + } + + private Money calculatePreMatureInterest(final LocalDate preMatureDate, final List transactions) { + final MathContext mc = MathContext.DECIMAL64; + final List postingPeriods = calculateInterestPayable(mc, preMatureDate, transactions); + + Money interestOnMaturity = Money.zero(this.currency); + + for (final PostingPeriod interestPostingPeriod : postingPeriods) { + final Money interestEarnedForPeriod = interestPostingPeriod.getInterestEarned(); + interestOnMaturity = interestOnMaturity.plus(interestEarnedForPeriod); + } + + return interestOnMaturity; + } + + @Override + public void postInterest(final MathContext mc, final LocalDate postingDate) { + final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate); + super.postInterest(mc, interestPostingUpToDate); + } + + @Override + public List calculateInterestUsing(final MathContext mc, final LocalDate postingDate) { + final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate); + return super.calculateInterestUsing(mc, interestPostingUpToDate); + } + + private LocalDate interestPostingUpToDate(final LocalDate interestPostingDate) { + LocalDate interestPostingUpToDate = interestPostingDate; + final LocalDate uptoMaturityDate = interestCalculatedUpto(); + if (uptoMaturityDate != null && uptoMaturityDate.isBefore(interestPostingDate)) { + interestPostingUpToDate = uptoMaturityDate; + } + return interestPostingUpToDate; + } + + public LocalDate maturityDate() { + return this.accountTermAndPreClosure.getMaturityLocalDate(); + } + + public BigDecimal maturityAmount() { + return this.accountTermAndPreClosure.maturityAmount(); + } + + private Money totalInterestPosted() { + Money interestPostedToDate = Money.zero(this.currency); + for (final SavingsAccountTransaction transaction : this.transactions) { + if (transaction.isInterestPostingAndNotReversed()) { + interestPostedToDate = interestPostedToDate.plus(transaction.getAmount(currency)); + } + } + + return interestPostedToDate; + } + + @Override + public Map activate(final AppUser currentUser, final JsonCommand command, final LocalDate tenantsTodayDate) { + final Map actualChanges = super.activate(currentUser, command, tenantsTodayDate); + final MathContext mc = MathContext.DECIMAL64; + List allTransactions = new ArrayList(); + allTransactions.addAll(retreiveOrderedNonInterestPostingTransactions()); + updateMaturityDateAndAmount(mc, allTransactions); + return actualChanges; + } + + private LocalDate depositStartDate() { + // TODO: Support to add deposit start date which can be a date after + // account activation date. + final LocalDate depositStartDate = accountSubmittedOrActivationDate(); + return depositStartDate; + } + + private LocalDate interestCalculatedUpto() { + LocalDate uptoMaturityDate = calculateMaturityDate(); + if (uptoMaturityDate != null) { + // interest should not be calculated for maturity day + uptoMaturityDate = uptoMaturityDate.minusDays(1); + } + return uptoMaturityDate; + } + + public void validateDomainRules() { + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME); + validateDomainRules(baseDataValidator); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + private void validateDomainRules(final DataValidatorBuilder baseDataValidator) { + + final boolean isMinTermGreaterThanMax = this.accountTermAndPreClosure.depositTermDetail() + .isMinDepositTermGreaterThanMaxDepositTerm(); + final boolean isValidDepositPeriod = this.accountTermAndPreClosure.depositTermDetail().isDepositBetweenMinAndMax( + depositStartDate(), calculateMaturityDate()); + // deposit period should be within min and max deposit term + if (isMinTermGreaterThanMax) { + final Integer maxTerm = this.accountTermAndPreClosure.depositTermDetail().maxDepositTerm(); + baseDataValidator.reset().parameter(maxDepositTermParamName).value(maxTerm) + .failWithCodeNoParameterAddedToErrorCode("max.term.lessthan.min.term"); + } + final Integer depositPeriod = this.accountTermAndPreClosure.depositPeriod(); + final SavingsPeriodFrequencyType depositPeriodFrequencyType = this.accountTermAndPreClosure.depositPeriodFrequencyType(); + + if (!isValidDepositPeriod) { + baseDataValidator.reset().parameter(depositPeriodParamName).value(depositPeriod) + .failWithCodeNoParameterAddedToErrorCode("deposit.period.not.between.min.and.max.deposit.term"); + } else { + final Integer inMultiplesOf = this.accountTermAndPreClosure.depositTermDetail().inMultiplesOfDepositTerm(); + if (inMultiplesOf != null) { + final boolean isValid = this.accountTermAndPreClosure.depositTermDetail().isValidInMultiplesOfPeriod(depositPeriod, + depositPeriodFrequencyType); + if (!isValid) { + baseDataValidator.reset().parameter(depositPeriodParamName).value(depositPeriod) + .failWithCodeNoParameterAddedToErrorCode("deposit.period.not.multiple.of.term"); + } + } + } + + if(this.chart == null){ + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("no.valid.interest.rate.slab.available"); + }else{ + final LocalDate chartFromDate = this.chart.getFromDateAsLocalDate(); + LocalDate chartEndDate = this.chart.getEndDateAsLocalDate(); + chartEndDate = chartEndDate == null ? DateUtils.getLocalDateOfTenant() : chartEndDate; + + final LocalDateInterval chartInterval = LocalDateInterval.create(chartFromDate, chartEndDate); + if(!chartInterval.contains(accountSubmittedOrActivationDate())){ + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("no.valid.interest.rate.slab.available"); + } + } + } + + public boolean isReinvestOnClosure() { + return this.accountTermAndPreClosure.isReinvestOnClosure(); + } + + public boolean isTransferToSavingsOnClosure() { + return this.accountTermAndPreClosure.isTransferToSavingsOnClosure(); + } + + public FixedDepositAccount reInvest(Money depositAmount) { + + final DepositAccountTermAndPreClosure newAccountTermAndPreClosure = this.accountTermAndPreClosure.deepCopy(depositAmount); + final SavingsProduct product = this.product; + final InterestRateChart productChart = product.applicableChart(getClosedOnDate()); + final DepositAccountInterestRateChart newChart = DepositAccountInterestRateChart.from(productChart); + + final AccountType accountType = AccountType.fromInt(this.accountType); + final SavingsPostingInterestPeriodType postingPeriodType = SavingsPostingInterestPeriodType.fromInt(this.interestPostingPeriodType); + final SavingsCompoundingInterestPeriodType compoundingPeriodType = SavingsCompoundingInterestPeriodType + .fromInt(this.interestCompoundingPeriodType); + final SavingsInterestCalculationType interestCalculationType = SavingsInterestCalculationType.fromInt(this.interestCalculationType); + final SavingsInterestCalculationDaysInYearType daysInYearType = SavingsInterestCalculationDaysInYearType + .fromInt(this.interestCalculationDaysInYearType); + final BigDecimal minRequiredOpeningBalance = null; + final BigDecimal interestRate = BigDecimal.ZERO; + final Set savingsAccountCharges = null; + final SavingsPeriodFrequencyType lockinPeriodFrequencyType = SavingsPeriodFrequencyType.fromInt(this.lockinPeriodFrequencyType); + final Integer lockinPeriodFrequency = this.lockinPeriodFrequency; + final boolean withdrawalFeeApplicableForTransfer = false; + final String accountNumber = null; + final FixedDepositAccount reInvestedAccount = FixedDepositAccount.createNewApplicationForSubmittal(client, group, product, fieldOfficer, accountNumber, externalId, + accountType, getClosedOnDate(), closedBy, interestRate, compoundingPeriodType, postingPeriodType, interestCalculationType, + daysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, + withdrawalFeeApplicableForTransfer, savingsAccountCharges, newAccountTermAndPreClosure, newChart); + + newAccountTermAndPreClosure.updateAccountReference(reInvestedAccount); + newChart.updateDepositAccountReference(reInvestedAccount); + + return reInvestedAccount; + + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/FixedDepositAccountRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/FixedDepositAccountRepository.java new file mode 100644 index 000000000..23f2e7582 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/FixedDepositAccountRepository.java @@ -0,0 +1,14 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface FixedDepositAccountRepository extends JpaRepository, JpaSpecificationExecutor { + + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/FixedDepositProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/FixedDepositProduct.java new file mode 100644 index 000000000..986c97de6 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/FixedDepositProduct.java @@ -0,0 +1,264 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.deleteParamName; +import static org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants.idParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.persistence.CascadeType; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.OneToMany; +import javax.persistence.OneToOne; +import javax.persistence.Transient; + +import org.joda.time.LocalDate; +import org.mifosplatform.accounting.common.AccountingRuleType; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; +import org.mifosplatform.portfolio.charge.domain.Charge; +import org.mifosplatform.portfolio.interestratechart.InterestRateChartApiConstants; +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChart; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartAssembler; +import org.mifosplatform.portfolio.savings.DepositsApiConstants; +import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; +import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; +import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +@Entity +@DiscriminatorValue("200") +public class FixedDepositProduct extends SavingsProduct { + + @OneToOne(mappedBy = "product", cascade = CascadeType.ALL) + private DepositProductTermAndPreClosure productTermAndPreClosure; + + @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @JoinTable(name = "m_deposit_product_interest_rate_chart", joinColumns = @JoinColumn(name = "deposit_product_id"), inverseJoinColumns = @JoinColumn(name = "interest_rate_chart_id", unique = true)) + protected Set charts; + + @Transient + protected InterestRateChartAssembler chartAssembler; + + protected FixedDepositProduct() { + super(); + } + + public static FixedDepositProduct createNew(final String name, final String shortName, final String description, + final MonetaryCurrency currency, final BigDecimal interestRate, + final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, + final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, + final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, + final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, + final AccountingRuleType accountingRuleType, final Set charges, + final DepositProductTermAndPreClosure productTermAndPreClosure, final Set charts) { + + final BigDecimal minRequiredOpeningBalance = null; + final boolean withdrawalFeeApplicableForTransfer = false; + final boolean allowOverdraft = false; + final BigDecimal overdraftLimit = null; + + return new FixedDepositProduct(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, + interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges, + productTermAndPreClosure, charts, allowOverdraft, overdraftLimit); + } + + protected FixedDepositProduct(final String name, final String shortName, final String description, final MonetaryCurrency currency, + final BigDecimal interestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, + final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, + final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, + final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, + final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set charges, + final DepositProductTermAndPreClosure productTermAndPreClosure, final Set charts, + final boolean allowOverdraft, final BigDecimal overdraftLimit) { + + super(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, + interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, + lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges, allowOverdraft, overdraftLimit); + + if (charts != null) { + this.charts = charts; + } + + this.productTermAndPreClosure = productTermAndPreClosure; + } + + public void addCharts(final Set newCharts) { + final Set existingCharts = setOfCharts(); + existingCharts.addAll(newCharts); + } + + public void addChart(final InterestRateChart newChart) { + final Set existingCharts = setOfCharts(); + existingCharts.add(newChart); + } + + public Set setOfCharts() { + if (this.charts == null) { + this.charts = new HashSet(); + } + return this.charts; + } + + @Override + public Map update(final JsonCommand command) { + final Map actualChanges = new LinkedHashMap(10); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME); + + actualChanges.putAll(this.update(command, baseDataValidator)); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + + return actualChanges; + } + + protected Map update(final JsonCommand command, final DataValidatorBuilder baseDataValidator) { + final Map actualChanges = new LinkedHashMap(10); + + actualChanges.putAll(super.update(command)); + + if (this.productTermAndPreClosure != null) { + actualChanges.putAll(this.productTermAndPreClosure.update(command, baseDataValidator)); + } + + // update chart Slabs + if (command.hasParameter(DepositsApiConstants.chartsParamName)) { + updateCharts(command, actualChanges, baseDataValidator); + } + + return actualChanges; + } + + private void updateCharts(JsonCommand command, Map actualChanges, final DataValidatorBuilder baseDataValidator) { + final Map deletedCharts = new HashMap(); + final Map chartsChanges = new HashMap(); + + if (command.hasParameter(DepositsApiConstants.chartsParamName)) { + final JsonArray array = command.arrayOfParameterNamed(DepositsApiConstants.chartsParamName); + if (array != null) { + for (int i = 0; i < array.size(); i++) { + final JsonObject chartElement = array.get(i).getAsJsonObject(); + JsonCommand chartCommand = JsonCommand.fromExistingCommand(command, chartElement); + if (chartCommand.parameterExists(idParamName)) { + final Long chartId = chartCommand.longValueOfParameterNamed(idParamName); + final InterestRateChart chart = this.findChart(chartId); + if (chart == null) { + baseDataValidator.parameter(idParamName).value(chartId).failWithCode("no.chart.associated.with.id"); + } else if (chartCommand.parameterExists(deleteParamName)) { + if (this.removeChart(chart)) { + deletedCharts.put(idParamName, chartId); + } + } else { + chart.update(chartCommand, chartsChanges, baseDataValidator, this.setOfCharts(), this.currency().getCode()); + } + } else { + // assemble chart + final InterestRateChart newChart = this.chartAssembler.assembleFrom(chartElement, this.currency().getCode()); + this.addChart(newChart); + } + } + } + } + + this.validateCharts(baseDataValidator); + + // add chart changes to actual changes list. + if (!chartsChanges.isEmpty()) { + actualChanges.put(InterestRateChartApiConstants.chartSlabs, chartsChanges); + } + + // add deleted chart to actual changes + if (!deletedCharts.isEmpty()) { + actualChanges.put("deletedChartSlabs", deletedCharts); + } + } + + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + @Override + public InterestRateChart findChart(Long chartId) { + final Set charts = setOfCharts(); + + for (InterestRateChart chart : charts) { + if (chart.getId().equals(chartId)) { return chart; } + } + return null; + } + + private boolean removeChart(InterestRateChart chart) { + Set charts = setOfCharts(); + return charts.remove(chart); + } + + public void setHelpers(final InterestRateChartAssembler chartAssembler) { + this.chartAssembler = chartAssembler; + } + + public void validateCharts(final DataValidatorBuilder baseDataValidator) { + final Set charts = this.setOfCharts(); + for (InterestRateChart existingChart : charts) { + this.validateChart(baseDataValidator, existingChart); + } + } + + public void validateChart(final DataValidatorBuilder baseDataValidator, final InterestRateChart comparingChart) { + final Set charts = this.setOfCharts(); + for (InterestRateChart existingChart : charts) { + if (!existingChart.equals(comparingChart)) { + if (existingChart.chartFields().isOverlapping(comparingChart.chartFields())) { + baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.overlapping.from.and.end.dates", + existingChart.getFromDateAsLocalDate(), existingChart.getEndDateAsLocalDate(), + comparingChart.getFromDateAsLocalDate(), comparingChart.getEndDateAsLocalDate()); + } + } + } + } + + @Override + public InterestRateChart applicableChart(final LocalDate target) { + InterestRateChart applicableChart = null; + if (this.charts != null) { + for (InterestRateChart chart : this.charts) { + if(chart.isApplicableChartFor(target)){ + applicableChart = chart; + break; + } + } + } + return applicableChart; + } + + public DepositProductTermAndPreClosure depositProductTermAndPreClosure() { + return this.productTermAndPreClosure; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/FixedDepositProductRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/FixedDepositProductRepository.java new file mode 100644 index 000000000..ee54e5969 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/FixedDepositProductRepository.java @@ -0,0 +1,13 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface FixedDepositProductRepository extends JpaRepository, JpaSpecificationExecutor { + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/RecurringDepositAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/RecurringDepositAccount.java new file mode 100644 index 000000000..96bb950bd --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/RecurringDepositAccount.java @@ -0,0 +1,820 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositPeriodParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.maxDepositTermParamName; +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.onAccountClosureIdParamName; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import javax.persistence.CascadeType; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.OneToOne; +import javax.persistence.Transient; + +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.domain.LocalDateInterval; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.core.service.DateUtils; +import org.mifosplatform.organisation.monetary.domain.Money; +import org.mifosplatform.organisation.staff.domain.Staff; +import org.mifosplatform.portfolio.accountdetails.domain.AccountType; +import org.mifosplatform.portfolio.client.domain.Client; +import org.mifosplatform.portfolio.group.domain.Group; +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChart; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartAssembler; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; +import org.mifosplatform.portfolio.savings.DepositAccountOnClosureType; +import org.mifosplatform.portfolio.savings.DepositsApiConstants; +import org.mifosplatform.portfolio.savings.PreClosurePenalInterestOnType; +import org.mifosplatform.portfolio.savings.SavingsApiConstants; +import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; +import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; +import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType; +import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionDTO; +import org.mifosplatform.portfolio.savings.domain.interest.PostingPeriod; +import org.mifosplatform.portfolio.savings.service.SavingsEnumerations; +import org.mifosplatform.useradministration.domain.AppUser; + +@Entity +@DiscriminatorValue("300") +public class RecurringDepositAccount extends SavingsAccount { + + @OneToOne(mappedBy = "account", cascade = CascadeType.ALL) + private DepositAccountTermAndPreClosure accountTermAndPreClosure; + + @OneToOne(mappedBy = "account", cascade = CascadeType.ALL) + private DepositAccountRecurringDetail recurringDetail; + + @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "account") + protected DepositAccountInterestRateChart chart; + + @Transient + protected InterestRateChartAssembler chartAssembler; + + protected RecurringDepositAccount() { + // + } + + public static RecurringDepositAccount createNewApplicationForSubmittal(final Client client, final Group group, + final SavingsProduct product, final Staff fieldOfficer, final String accountNo, final String externalId, + final AccountType accountType, final LocalDate submittedOnDate, final AppUser submittedBy, final BigDecimal interestRate, + final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, + final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, + final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, + final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, + final boolean withdrawalFeeApplicableForTransfer, final Set savingsAccountCharges, + final DepositAccountTermAndPreClosure accountTermAndPreClosure, final DepositAccountRecurringDetail recurringDetail, + final DepositAccountInterestRateChart chart) { + + final boolean allowOverdraft = false; + final BigDecimal overdraftLimit = new BigDecimal(0); + + final SavingsAccountStatusType status = SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL; + return new RecurringDepositAccount(client, group, product, fieldOfficer, accountNo, externalId, status, accountType, + submittedOnDate, submittedBy, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, + interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, + lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, savingsAccountCharges, accountTermAndPreClosure, + recurringDetail, chart, allowOverdraft, overdraftLimit); + } + + private RecurringDepositAccount(final Client client, final Group group, final SavingsProduct product, final Staff fieldOfficer, + final String accountNo, final String externalId, final SavingsAccountStatusType status, final AccountType accountType, + final LocalDate submittedOnDate, final AppUser submittedBy, final BigDecimal nominalAnnualInterestRate, + final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, + final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, + final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, + final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, + final boolean withdrawalFeeApplicableForTransfer, final Set savingsAccountCharges, + final DepositAccountTermAndPreClosure accountTermAndPreClosure, final DepositAccountRecurringDetail recurringDetail, + final DepositAccountInterestRateChart chart, final boolean allowOverdraft, final BigDecimal overdraftLimit) { + + super(client, group, product, fieldOfficer, accountNo, externalId, status, accountType, submittedOnDate, submittedBy, + nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, + interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, + withdrawalFeeApplicableForTransfer, savingsAccountCharges, allowOverdraft, overdraftLimit); + + this.accountTermAndPreClosure = accountTermAndPreClosure; + this.recurringDetail = recurringDetail; + this.chart = chart; + if (this.chart != null) { + this.chart.updateDepositAccountReference(this); + } + } + + @Override + public void modifyApplication(final JsonCommand command, final Map actualChanges) { + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.modifyApplicationAction); + super.modifyApplication(command, actualChanges, baseDataValidator); + + accountTermAndPreClosure.update(command, baseDataValidator); + recurringDetail.update(command, baseDataValidator); + + validateDomainRules(baseDataValidator); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + @Override + protected BigDecimal getEffectiveInterestRateAsFraction(final MathContext mc, final LocalDate interestPostingUpToDate) { + + final boolean isPreMatureClosure = SavingsAccountStatusType.fromInt(this.status).isPreMatureClosure(); + boolean applyPreMaturePenalty = false; + BigDecimal penalInterest = BigDecimal.ZERO; + LocalDate depositCloseDate = calculateMaturityDate(); + if (isPreMatureClosure) { + if (this.accountTermAndPreClosure.isPreClosurePenalApplicable()) { + applyPreMaturePenalty = true; + penalInterest = this.accountTermAndPreClosure.depositPreClosureDetail().preClosurePenalInterest(); + final PreClosurePenalInterestOnType preClosurePenalInterestOnType = this.accountTermAndPreClosure.depositPreClosureDetail() + .preClosurePenalInterestOnType(); + if (preClosurePenalInterestOnType.isWholeTerm()) { + depositCloseDate = interestCalculatedUpto(); + } else if (preClosurePenalInterestOnType.isTillPrematureWithdrawal()) { + depositCloseDate = interestPostingUpToDate; + } + } + } + + final BigDecimal depositAmount = accountTermAndPreClosure.depositAmount(); + + BigDecimal applicableInterestRate = this.chart.getApplicableInterestRate(depositAmount, depositStartDate(), depositCloseDate); + + if (applyPreMaturePenalty) { + applicableInterestRate = applicableInterestRate.subtract(penalInterest); + applicableInterestRate = applicableInterestRate.compareTo(BigDecimal.ZERO) == -1 ? BigDecimal.ZERO : applicableInterestRate; + } + + this.nominalAnnualInterestRate = applicableInterestRate; + + return applicableInterestRate.divide(BigDecimal.valueOf(100l), mc); + } + + public void updateMaturityDateAndAmount(final MathContext mc, final LocalDate transactionStartDate) { + final LocalDate maturityDate = calculateMaturityDate(); + final LocalDate interestCalculationUpto = maturityDate.minusDays(1); + final List allTransactions = generateIncludingFutureTransactions(transactionStartDate, + interestCalculationUpto); + + final List postingPeriods = calculateInterestPayable(mc, interestCalculationUpto, allTransactions); + Money totalInterestPayable = Money.zero(getCurrency()); + Money totalDepositAmount = Money.zero(getCurrency()); + for (PostingPeriod postingPeriod : postingPeriods) { + totalInterestPayable = totalInterestPayable.plus(postingPeriod.getInterestEarned()); + totalDepositAmount = totalDepositAmount.plus(postingPeriod.closingBalance()).minus(postingPeriod.openingBalance()); + } + + this.accountTermAndPreClosure.updateMaturityDetails(totalDepositAmount.getAmount(), totalInterestPayable.getAmount(), maturityDate); + } + + public void updateMaturityStatus() { + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.updateMaturityDetailsAction); + + final SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); + if (!SavingsAccountStatusType.ACTIVE.hasStateOf(currentStatus)) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("not.in.active.state"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + final LocalDate todayDate = DateUtils.getLocalDateOfTenant(); + if (!this.maturityDate().isAfter(todayDate)) { + // update account status + this.status = SavingsAccountStatusType.MATURED.getValue(); + postMaturityInterest(maturityDate()); + } + } + + public LocalDate calculateMaturityDate() { + + final LocalDate startDate = depositStartDate(); + LocalDate maturityDate = null; + final Integer depositPeriod = this.accountTermAndPreClosure.depositPeriod(); + switch (this.accountTermAndPreClosure.depositPeriodFrequencyType()) { + case DAYS: + maturityDate = startDate.plusDays(depositPeriod); + break; + case WEEKS: + maturityDate = startDate.plusWeeks(depositPeriod); + break; + case MONTHS: + maturityDate = startDate.plusMonths(depositPeriod); + break; + case YEARS: + maturityDate = startDate.plusYears(depositPeriod); + break; + case INVALID: + break; + } + + return maturityDate; + } + + private List calculateInterestPayable(final MathContext mc, final LocalDate maturityDate, + final List transactions) { + + // 1. default to calculate interest based on entire history OR + // 2. determine latest 'posting period' and find interest credited to + // that period + + // A generate list of EndOfDayBalances (not including interest postings) + final SavingsPostingInterestPeriodType postingPeriodType = SavingsPostingInterestPeriodType.fromInt(this.interestPostingPeriodType); + + final SavingsCompoundingInterestPeriodType compoundingPeriodType = SavingsCompoundingInterestPeriodType + .fromInt(this.interestCompoundingPeriodType); + + final SavingsInterestCalculationDaysInYearType daysInYearType = SavingsInterestCalculationDaysInYearType + .fromInt(this.interestCalculationDaysInYearType); + + final List postingPeriodIntervals = this.savingsHelper.determineInterestPostingPeriods( + accountSubmittedOrActivationDate(), maturityDate, postingPeriodType); + + final List allPostingPeriods = new ArrayList(); + + Money periodStartingBalance = Money.zero(currency); + + final SavingsInterestCalculationType interestCalculationType = SavingsInterestCalculationType.fromInt(this.interestCalculationType); + final BigDecimal interestRateAsFraction = getEffectiveInterestRateAsFraction(mc, maturityDate); + + for (final LocalDateInterval periodInterval : postingPeriodIntervals) { + final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance, transactions, + this.currency, compoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYearType.getValue(), + maturityDate); + + periodStartingBalance = postingPeriod.closingBalance(); + + allPostingPeriods.add(postingPeriod); + } + + this.savingsHelper.calculateInterestForAllPostingPeriods(this.currency, allPostingPeriods); + // this.summary.updateFromInterestPeriodSummaries(this.currency, + // allPostingPeriods); + return allPostingPeriods; + } + + private List generateIncludingFutureTransactions(final LocalDate transactionStartDate, + final LocalDate depositEndDate) { + LocalDate transactionDate = transactionStartDate; + final BigDecimal recurringDepositAmount = this.recurringDetail.recurringDepositAmount(); + List allTransactions = new ArrayList(); + // add existing transactions + allTransactions.addAll(retreiveOrderedNonInterestPostingTransactions()); + while (!transactionDate.isAfter(depositEndDate) && !transactionDate.isEqual(depositEndDate)) { + final Money transactionAmountMoney = Money.of(this.currency, recurringDepositAmount); + final SavingsAccountTransaction transaction = SavingsAccountTransaction.deposit(null, office(), null, transactionDate, + transactionAmountMoney, new Date()); + allTransactions.add(transaction); + transactionDate = nextDepositDate(transactionDate); + } + + allTransactions = sortTransactions(allTransactions); + Money runningBalance = Money.zero(getCurrency()); + for (final SavingsAccountTransaction transaction : allTransactions) { + if (transaction.isReversed()) { + transaction.zeroBalanceFields(); + } else { + + Money transactionAmount = Money.zero(this.currency); + if (transaction.isCredit()) { + transactionAmount = transactionAmount.plus(transaction.getAmount(this.currency)); + } else if (transaction.isDebit()) { + transactionAmount = transactionAmount.minus(transaction.getAmount(this.currency)); + } + + runningBalance = runningBalance.plus(transactionAmount); + transaction.updateRunningBalance(runningBalance); + } + } + // loop over transactions in reverse + LocalDate endOfBalanceDate = depositEndDate; + for (int i = allTransactions.size() - 1; i >= 0; i--) { + final SavingsAccountTransaction transaction = allTransactions.get(i); + if (transaction.isNotReversed()) { + transaction.updateCumulativeBalanceAndDates(this.currency, endOfBalanceDate); + endOfBalanceDate = transaction.transactionLocalDate().minusDays(1); + } + } + return allTransactions; + } + + private LocalDate nextDepositDate(LocalDate depositDate) { + final LocalDate depositStartDate = depositStartDate(); + LocalDate nextDepositDate = depositStartDate; + while (!depositDate.isBefore(nextDepositDate)) { + nextDepositDate = calculateNextDepositDate(nextDepositDate); + } + return nextDepositDate; + } + + private LocalDate calculateNextDepositDate(final LocalDate depositDate) { + final Integer depositFrequency = this.recurringDetail.recurringDepositFrequency(); + LocalDate nextDepositDate = depositDate; + final SavingsPeriodFrequencyType depositFrequencyType = this.recurringDetail.recurringDepositFrequencyType(); + switch (depositFrequencyType) { + case DAYS: + nextDepositDate = depositDate.plusDays(depositFrequency); + break; + case WEEKS: + nextDepositDate = depositDate.plusWeeks(depositFrequency); + break; + case MONTHS: + nextDepositDate = depositDate.plusMonths(depositFrequency); + break; + case YEARS: + nextDepositDate = depositDate.plusYears(depositFrequency); + break; + case INVALID: + nextDepositDate = null; + break; + } + return nextDepositDate; + } + + public LocalDate depositStartDate() { + // TODO: Support to add deposit start date which can be a date after + // account activation date. + final LocalDate depositStartDate = accountSubmittedOrActivationDate(); + return depositStartDate; + } + + public SavingsAccountTransaction prematureClosure(final AppUser currentUser, final JsonCommand command, + final LocalDate tenantsTodayDate, final PaymentDetail paymentDetail, final Map actualChanges) { + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME + DepositsApiConstants.preMatureCloseAction); + + final SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); + if (!SavingsAccountStatusType.ACTIVE.hasStateOf(currentStatus)) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("not.in.active.state"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); + final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName); + + if (closedDate.isBefore(getActivationLocalDate())) { + baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) + .failWithCode("must.be.after.activation.date"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + if (closedDate.isAfter(maturityDate())) { + baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) + .failWithCode("must.be.before.maturity.date"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + if (closedDate.isAfter(tenantsTodayDate)) { + baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) + .failWithCode("cannot.be.a.future.date"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + final List savingsAccountTransactions = retreiveListOfTransactions(); + if (savingsAccountTransactions.size() > 0) { + final SavingsAccountTransaction accountTransaction = savingsAccountTransactions.get(savingsAccountTransactions.size() - 1); + if (accountTransaction.isAfter(closedDate)) { + baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) + .failWithCode("must.be.after.last.transaction.date"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + } + + validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_CLOSE_ACCOUNT, closedDate); + this.status = SavingsAccountStatusType.PRE_MATURE_CLOSURE.getValue(); + + final Integer onAccountClosureId = command.integerValueOfParameterNamed(onAccountClosureIdParamName); + final DepositAccountOnClosureType onClosureType = DepositAccountOnClosureType.fromInt(onAccountClosureId); + this.accountTermAndPreClosure.updateOnAccountClosureStatus(onClosureType); + + postPreMaturityInterest(closedDate); + + // withdraw deposit amount before closing the account + final Money transactionAmountMoney = Money.of(this.currency, this.getAccountBalance()); + final SavingsAccountTransaction withdraw = SavingsAccountTransaction.withdrawal(this, office(), paymentDetail, closedDate, + transactionAmountMoney, new Date()); + this.transactions.add(withdraw); + + actualChanges.put(SavingsApiConstants.statusParamName, SavingsEnumerations.status(this.status)); + actualChanges.put(SavingsApiConstants.localeParamName, command.locale()); + actualChanges.put(SavingsApiConstants.dateFormatParamName, command.dateFormat()); + actualChanges.put(SavingsApiConstants.closedOnDateParamName, closedDate.toString(fmt)); + + this.rejectedOnDate = null; + this.rejectedBy = null; + this.withdrawnOnDate = null; + this.withdrawnBy = null; + this.closedOnDate = closedDate.toDate(); + this.closedBy = currentUser; + + return withdraw; + } + + @Override + protected void processAccountUponActivation(final DateTimeFormatter fmt) { + final Money recurringDepositAmount = Money.of(this.currency, this.recurringDetail.recurringDepositAmount()); + if (recurringDepositAmount.isGreaterThanZero()) { + + final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, getActivationLocalDate(), + recurringDepositAmount.getAmount(), null, new Date()); + deposit(transactionDTO); + final LocalDate interestPostingUpToDate = interestPostingUpToDate(DateUtils.getLocalDateOfTenant()); + // update existing transactions so derived balance fields are + // correct. + recalculateDailyBalances(Money.zero(this.currency), interestPostingUpToDate); + } + } + + public SavingsAccountTransaction close(final AppUser currentUser, final JsonCommand command, final LocalDate tenantsTodayDate, + final PaymentDetail paymentDetail, final Map actualChanges) { + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.closeAction); + + final SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); + if (!SavingsAccountStatusType.MATURED.hasStateOf(currentStatus)) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("not.in.matured.state"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); + final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName); + + if (closedDate.isBefore(getActivationLocalDate())) { + baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) + .failWithCode("must.be.after.activation.date"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + if (closedDate.isBefore(maturityDate())) { + baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) + .failWithCode("must.be.after.account.maturity.date"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + if (closedDate.isAfter(tenantsTodayDate)) { + baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) + .failWithCode("cannot.be.a.future.date"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + final List savingsAccountTransactions = retreiveListOfTransactions(); + if (savingsAccountTransactions.size() > 0) { + final SavingsAccountTransaction accountTransaction = savingsAccountTransactions.get(savingsAccountTransactions.size() - 1); + if (accountTransaction.isAfter(closedDate)) { + baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) + .failWithCode("must.be.after.last.transaction.date"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + } + + validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_CLOSE_ACCOUNT, closedDate); + this.status = SavingsAccountStatusType.CLOSED.getValue(); + + final Integer onAccountClosureId = command.integerValueOfParameterNamed(onAccountClosureIdParamName); + final DepositAccountOnClosureType onClosureType = DepositAccountOnClosureType.fromInt(onAccountClosureId); + this.accountTermAndPreClosure.updateOnAccountClosureStatus(onClosureType); + + postMaturityInterest(maturityDate()); + + // withdraw deposit amount before closing the account + final Money transactionAmountMoney = Money.of(this.currency, this.getAccountBalance()); + final SavingsAccountTransaction withdraw = SavingsAccountTransaction.withdrawal(this, office(), paymentDetail, closedDate, + transactionAmountMoney, new Date()); + this.transactions.add(withdraw); + + actualChanges.put(SavingsApiConstants.statusParamName, SavingsEnumerations.status(this.status)); + actualChanges.put(SavingsApiConstants.localeParamName, command.locale()); + actualChanges.put(SavingsApiConstants.dateFormatParamName, command.dateFormat()); + actualChanges.put(SavingsApiConstants.closedOnDateParamName, closedDate.toString(fmt)); + + this.rejectedOnDate = null; + this.rejectedBy = null; + this.withdrawnOnDate = null; + this.withdrawnBy = null; + this.closedOnDate = closedDate.toDate(); + this.closedBy = currentUser; + + return withdraw; + } + + public void postMaturityInterest(final LocalDate interestPostingUpToDate) { + + final MathContext mc = MathContext.DECIMAL64; + + final List postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate); + + Money interestPostedToDate = Money.zero(this.currency); + + boolean recalucateDailyBalanceDetails = false; + + for (final PostingPeriod interestPostingPeriod : postingPeriods) { + + LocalDate interestPostingTransactionDate = interestPostingPeriod.dateOfPostingTransaction(); + interestPostingTransactionDate = interestPostingTransactionDate.isAfter(interestPostingUpToDate) ? interestPostingUpToDate + : interestPostingTransactionDate; + final Money interestEarnedToBePostedForPeriod = interestPostingPeriod.getInterestEarned(); + + interestPostedToDate = interestPostedToDate.plus(interestEarnedToBePostedForPeriod); + + final SavingsAccountTransaction postingTransaction = findInterestPostingTransactionFor(interestPostingTransactionDate); + if (postingTransaction == null) { + final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), + interestPostingTransactionDate, interestEarnedToBePostedForPeriod); + this.transactions.add(newPostingTransaction); + recalucateDailyBalanceDetails = true; + } else { + final boolean correctionRequired = postingTransaction.hasNotAmount(interestEarnedToBePostedForPeriod); + if (correctionRequired) { + postingTransaction.reverse(); + final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), + interestPostingTransactionDate, interestEarnedToBePostedForPeriod); + this.transactions.add(newPostingTransaction); + recalucateDailyBalanceDetails = true; + } + } + } + + if (recalucateDailyBalanceDetails) { + // update existing transactions so derived balance fields are + // correct. + recalculateDailyBalances(Money.zero(this.currency), interestPostingUpToDate); + } + + this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); + } + + public void postPreMaturityInterest(final LocalDate accountCloseDate) { + + final Money interestPostedToDate = totalInterestPosted(); + final Money interestOnMaturity = calculatePreMatureInterest(accountCloseDate, retreiveOrderedNonInterestPostingTransactions()); + + boolean recalucateDailyBalance = false; + + // post remaining interest + final Money remainigInterestToBePosted = interestOnMaturity.minus(interestPostedToDate); + if (!remainigInterestToBePosted.isZero()) { + final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), + accountCloseDate, remainigInterestToBePosted); + this.transactions.add(newPostingTransaction); + recalucateDailyBalance = true; + } + + if (recalucateDailyBalance) { + // update existing transactions so derived balance fields are + // correct. + recalculateDailyBalances(Money.zero(this.currency), accountCloseDate); + } + + this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); + this.accountTermAndPreClosure.updateMaturityDetails(this.getAccountBalance(), accountCloseDate); + } + + public BigDecimal calculatePreMatureAmount(final LocalDate preMatureDate) { + + final Money interestPostedToDate = totalInterestPosted().copy(); + + final Money interestEarnedTillDate = calculatePreMatureInterest(preMatureDate, retreiveOrderedNonInterestPostingTransactions()); + + final Money accountBalance = Money.of(getCurrency(), getAccountBalance()); + final Money maturityAmount = accountBalance.minus(interestPostedToDate).plus(interestEarnedTillDate); + + return maturityAmount.getAmount(); + } + + private Money calculatePreMatureInterest(final LocalDate preMatureDate, final List transactions) { + final MathContext mc = MathContext.DECIMAL64; + final List postingPeriods = calculateInterestPayable(mc, preMatureDate, transactions); + + Money interestOnMaturity = Money.zero(this.currency); + + for (final PostingPeriod interestPostingPeriod : postingPeriods) { + final Money interestEarnedForPeriod = interestPostingPeriod.getInterestEarned(); + interestOnMaturity = interestOnMaturity.plus(interestEarnedForPeriod); + } + + return interestOnMaturity; + } + + @Override + public void postInterest(final MathContext mc, final LocalDate postingDate) { + final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate); + super.postInterest(mc, interestPostingUpToDate); + } + + @Override + public List calculateInterestUsing(final MathContext mc, final LocalDate postingDate) { + final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate); + return super.calculateInterestUsing(mc, interestPostingUpToDate); + } + + private LocalDate interestPostingUpToDate(final LocalDate interestPostingDate) { + LocalDate interestPostingUpToDate = interestPostingDate; + final LocalDate uptoMaturityDate = interestCalculatedUpto(); + if (uptoMaturityDate != null && uptoMaturityDate.isBefore(interestPostingDate)) { + interestPostingUpToDate = uptoMaturityDate; + } + return interestPostingUpToDate; + } + + private LocalDate maturityDate() { + return this.accountTermAndPreClosure.getMaturityLocalDate(); + } + + private LocalDate interestCalculatedUpto() { + LocalDate uptoMaturityDate = calculateMaturityDate(); + if (uptoMaturityDate != null) { + // interest should not be calculated for maturity day + uptoMaturityDate = uptoMaturityDate.minusDays(1); + } + return uptoMaturityDate; + } + + private Money totalInterestPosted() { + Money interestPostedToDate = Money.zero(this.currency); + for (final SavingsAccountTransaction transaction : this.transactions) { + if (transaction.isInterestPostingAndNotReversed()) { + interestPostedToDate = interestPostedToDate.plus(transaction.getAmount(currency)); + } + } + + return interestPostedToDate; + } + + @Override + public Map activate(final AppUser currentUser, final JsonCommand command, final LocalDate tenantsTodayDate) { + final Map actualChanges = super.activate(currentUser, command, tenantsTodayDate); + final MathContext mc = MathContext.DECIMAL64; + final LocalDate transactionStartDate = getActivationLocalDate(); + + // TODO: If first deposit and activation date are not same then + // recalculate the nextTransaction date + + final LocalDate nextTransactionStartDate = nextDepositDate(transactionStartDate); + updateMaturityDateAndAmount(mc, nextTransactionStartDate); + return actualChanges; + } + + protected List sortTransactions(final List transactions) { + final List listOfTransactionsSorted = new ArrayList(); + listOfTransactionsSorted.addAll(transactions); + + final SavingsAccountTransactionComparator transactionComparator = new SavingsAccountTransactionComparator(); + Collections.sort(listOfTransactionsSorted, transactionComparator); + return listOfTransactionsSorted; + } + + @Override + public SavingsAccountTransaction deposit(final SavingsAccountTransactionDTO transactionDTO) { + + if (isAccountMatured()) { + final String defaultUserMessage = "Transaction is not allowed. Account is matured."; + final ApiParameterError error = ApiParameterError.parameterError( + "error.msg.recurring.deposit.account.transaction.account.is.matured", defaultUserMessage, "transactionDate", + transactionDTO.getTransactionDate().toString(transactionDTO.getFormatter())); + + final List dataValidationErrors = new ArrayList(); + dataValidationErrors.add(error); + + throw new PlatformApiDataValidationException(dataValidationErrors); + } + + if (!isBeforeMaturityDate(transactionDTO.getTransactionDate())) { + final String defaultUserMessage = "Transaction is not allowed. Transaction date is on or after account maturity date."; + final ApiParameterError error = ApiParameterError.parameterError( + "error.msg.recurring.deposit.account.transaction.date.is.on.or.after.account.maturity.date", defaultUserMessage, + "transactionDate", transactionDTO.getTransactionDate().toString(transactionDTO.getFormatter())); + + final List dataValidationErrors = new ArrayList(); + dataValidationErrors.add(error); + + throw new PlatformApiDataValidationException(dataValidationErrors); + } + final SavingsAccountTransaction transaction = super.deposit(transactionDTO); + final LocalDate nextTransactionDate = nextDepositDate(transactionDTO.getTransactionDate()); + + final MathContext mc = MathContext.DECIMAL64; + updateMaturityDateAndAmount(mc, nextTransactionDate); + + return transaction; + } + + private boolean isAccountMatured() { + return SavingsAccountStatusType.fromInt(status).isMatured(); + } + + private boolean isBeforeMaturityDate(final LocalDate compareDate) { + final LocalDate maturityDate = this.maturityDate(); + return compareDate.isBefore(maturityDate); + } + + public void validateDomainRules() { + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME); + validateDomainRules(baseDataValidator); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + private void validateDomainRules(final DataValidatorBuilder baseDataValidator) { + + final boolean isMinTermGreaterThanMax = this.accountTermAndPreClosure.depositTermDetail() + .isMinDepositTermGreaterThanMaxDepositTerm(); + final boolean isValidDepositPeriod = this.accountTermAndPreClosure.depositTermDetail().isDepositBetweenMinAndMax( + depositStartDate(), calculateMaturityDate()); + // deposit period should be within min and max deposit term + if (isMinTermGreaterThanMax) { + final Integer maxTerm = this.accountTermAndPreClosure.depositTermDetail().maxDepositTerm(); + baseDataValidator.reset().parameter(maxDepositTermParamName).value(maxTerm) + .failWithCodeNoParameterAddedToErrorCode("max.term.lessthan.min.term"); + } + final Integer depositPeriod = this.accountTermAndPreClosure.depositPeriod(); + final SavingsPeriodFrequencyType depositPeriodFrequencyType = this.accountTermAndPreClosure.depositPeriodFrequencyType(); + + if (!isValidDepositPeriod) { + baseDataValidator.reset().parameter(depositPeriodParamName).value(depositPeriod) + .failWithCodeNoParameterAddedToErrorCode("deposit.period.not.between.min.and.max.deposit.term"); + } else { + final Integer inMultiplesOf = this.accountTermAndPreClosure.depositTermDetail().inMultiplesOfDepositTerm(); + if (inMultiplesOf != null) { + final boolean isValid = this.accountTermAndPreClosure.depositTermDetail().isValidInMultiplesOfPeriod(depositPeriod, + depositPeriodFrequencyType); + if (!isValid) { + baseDataValidator.reset().parameter(depositPeriodParamName).value(depositPeriod) + .failWithCodeNoParameterAddedToErrorCode("deposit.period.not.multiple.of.term"); + } + } + } + + final LocalDate chartFromDate = this.chart.getFromDateAsLocalDate(); + LocalDate chartEndDate = this.chart.getEndDateAsLocalDate(); + chartEndDate = chartEndDate == null ? DateUtils.getLocalDateOfTenant() : chartEndDate; + + final LocalDateInterval chartInterval = LocalDateInterval.create(chartFromDate, chartEndDate); + if (!chartInterval.contains(accountSubmittedOrActivationDate())) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("no.valid.interest.rate.slab.available.for.date.range"); + } + } + + public boolean isReinvestOnClosure() { + return this.accountTermAndPreClosure.isReinvestOnClosure(); + } + + public boolean isTransferToSavingsOnClosure() { + return this.accountTermAndPreClosure.isTransferToSavingsOnClosure(); + } + + public RecurringDepositAccount reInvest(Money depositAmount) { + + final DepositAccountTermAndPreClosure newAccountTermAndPreClosure = this.accountTermAndPreClosure.deepCopy(depositAmount); + final DepositAccountRecurringDetail recurringDetail = this.recurringDetail.copy(); + final SavingsProduct product = this.product; + final InterestRateChart productChart = product.applicableChart(getClosedOnDate()); + final DepositAccountInterestRateChart newChart = DepositAccountInterestRateChart.from(productChart); + final String accountNumber = this.accountNumber; + final String externalId = this.externalId; + final AccountType accountType = AccountType.fromInt(this.accountType); + final SavingsPostingInterestPeriodType postingPeriodType = SavingsPostingInterestPeriodType.fromInt(this.interestPostingPeriodType); + final SavingsCompoundingInterestPeriodType compoundingPeriodType = SavingsCompoundingInterestPeriodType + .fromInt(this.interestCompoundingPeriodType); + final SavingsInterestCalculationType interestCalculationType = SavingsInterestCalculationType.fromInt(this.interestCalculationType); + final SavingsInterestCalculationDaysInYearType daysInYearType = SavingsInterestCalculationDaysInYearType + .fromInt(this.interestCalculationDaysInYearType); + final BigDecimal minRequiredOpeningBalance = null; + final BigDecimal interestRate = null; + final Set savingsAccountCharges = null; + final SavingsPeriodFrequencyType lockinPeriodFrequencyType = SavingsPeriodFrequencyType.fromInt(this.lockinPeriodFrequencyType); + final Integer lockinPeriodFrequency = this.lockinPeriodFrequency; + final boolean withdrawalFeeApplicableForTransfer = false; + + return RecurringDepositAccount.createNewApplicationForSubmittal(client, group, product, fieldOfficer, accountNumber, externalId, + accountType, getClosedOnDate(), closedBy, interestRate, compoundingPeriodType, postingPeriodType, interestCalculationType, + daysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, + withdrawalFeeApplicableForTransfer, savingsAccountCharges, newAccountTermAndPreClosure, recurringDetail, newChart); + + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/RecurringDepositAccountRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/RecurringDepositAccountRepository.java new file mode 100644 index 000000000..555929484 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/RecurringDepositAccountRepository.java @@ -0,0 +1,13 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface RecurringDepositAccountRepository extends JpaRepository, JpaSpecificationExecutor { + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/RecurringDepositProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/RecurringDepositProduct.java new file mode 100644 index 000000000..80defd3d1 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/RecurringDepositProduct.java @@ -0,0 +1,120 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.persistence.CascadeType; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; +import javax.persistence.OneToOne; + +import org.mifosplatform.accounting.common.AccountingRuleType; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; +import org.mifosplatform.portfolio.charge.domain.Charge; +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChart; +import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; +import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; +import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType; + +@Entity +@DiscriminatorValue("300") +public class RecurringDepositProduct extends FixedDepositProduct { + + @OneToOne(mappedBy = "product", cascade = CascadeType.ALL) + private DepositProductRecurringDetail recurringDetail; + + protected RecurringDepositProduct() { + super(); + } + + public static RecurringDepositProduct createNew(final String name, final String shortName, final String description, + final MonetaryCurrency currency, final BigDecimal interestRate, + final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, + final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, + final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final Integer lockinPeriodFrequency, + final SavingsPeriodFrequencyType lockinPeriodFrequencyType, final AccountingRuleType accountingRuleType, + final Set charges, final DepositProductTermAndPreClosure productTermAndPreClosure, + final DepositProductRecurringDetail recurringDetail, final Set charts) { + + final BigDecimal minRequiredOpeningBalance = null; + final boolean withdrawalFeeApplicableForTransfer = false; + final boolean allowOverdraft = false; + final BigDecimal overdraftLimit = null; + + return new RecurringDepositProduct(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, + interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges, + productTermAndPreClosure, recurringDetail, charts, allowOverdraft, overdraftLimit); + } + + protected RecurringDepositProduct(final String name, final String shortName, final String description, final MonetaryCurrency currency, + final BigDecimal interestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, + final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, + final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, + final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, + final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set charges, + final DepositProductTermAndPreClosure productTermAndPreClosure, final DepositProductRecurringDetail recurringDetail, + final Set charts, final boolean allowOverdraft, final BigDecimal overdraftLimit) { + + super(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, + interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, + lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges, productTermAndPreClosure, + charts, allowOverdraft, overdraftLimit); + + this.recurringDetail = recurringDetail; + } + + @Override + public Map update(final JsonCommand command) { + final Map actualChanges = new LinkedHashMap(10); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME); + + actualChanges.putAll(this.update(command, baseDataValidator)); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + + return actualChanges; + } + + @Override + protected Map update(final JsonCommand command, final DataValidatorBuilder baseDataValidator) { + final Map actualChanges = new LinkedHashMap(10); + + actualChanges.putAll(super.update(command, baseDataValidator)); + + if (this.recurringDetail != null) { + actualChanges.putAll(this.recurringDetail.update(command, baseDataValidator)); + } + + return actualChanges; + } + + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + public DepositProductRecurringDetail depositRecurringDetail() { + return this.recurringDetail; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/RecurringDepositProductRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/RecurringDepositProductRepository.java new file mode 100644 index 000000000..5b41ea5f7 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/RecurringDepositProductRepository.java @@ -0,0 +1,13 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface RecurringDepositProductRepository extends JpaRepository, JpaSpecificationExecutor { + // +} \ No newline at end of file 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 e8e69665f..db1a992e3 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 @@ -30,8 +30,13 @@ import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; +import javax.persistence.DiscriminatorColumn; +import javax.persistence.DiscriminatorType; +import javax.persistence.DiscriminatorValue; import javax.persistence.Embedded; import javax.persistence.Entity; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; @@ -67,6 +72,7 @@ import org.mifosplatform.portfolio.charge.exception.SavingsAccountChargeNotFound import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType; import org.mifosplatform.portfolio.group.domain.Group; +import org.mifosplatform.portfolio.savings.DepositAccountType; import org.mifosplatform.portfolio.savings.SavingsApiConstants; import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType; @@ -88,89 +94,92 @@ import com.google.gson.JsonArray; @Entity @Table(name = "m_savings_account", uniqueConstraints = { @UniqueConstraint(columnNames = { "account_no" }, name = "sa_account_no_UNIQUE"), @UniqueConstraint(columnNames = { "external_id" }, name = "sa_external_id_UNIQUE") }) +@Inheritance(strategy = InheritanceType.SINGLE_TABLE) +@DiscriminatorColumn(name = "deposit_type_enum", discriminatorType = DiscriminatorType.INTEGER) +@DiscriminatorValue("100") public class SavingsAccount extends AbstractPersistable { @Column(name = "account_no", length = 20, unique = true, nullable = false) - private String accountNumber; + protected String accountNumber; @Column(name = "external_id", nullable = true) - private String externalId; + protected String externalId; @ManyToOne(optional = true) @JoinColumn(name = "client_id", nullable = true) - private Client client; + protected Client client; @ManyToOne(optional = true) @JoinColumn(name = "group_id", nullable = true) - private Group group; + protected Group group; @ManyToOne @JoinColumn(name = "product_id", nullable = false) - private SavingsProduct product; + protected SavingsProduct product; @ManyToOne @JoinColumn(name = "field_officer_id", nullable = true) - private Staff fieldOfficer; + protected Staff fieldOfficer; @Column(name = "status_enum", nullable = false) - private Integer status; + protected Integer status; @Column(name = "account_type_enum", nullable = false) - private Integer accountType; + protected Integer accountType; @Temporal(TemporalType.DATE) @Column(name = "submittedon_date", nullable = true) - private Date submittedOnDate; + protected Date submittedOnDate; @ManyToOne(optional = true) @JoinColumn(name = "submittedon_userid", nullable = true) - private AppUser submittedBy; + protected AppUser submittedBy; @Temporal(TemporalType.DATE) @Column(name = "rejectedon_date") - private Date rejectedOnDate; + protected Date rejectedOnDate; @ManyToOne(optional = true) @JoinColumn(name = "rejectedon_userid", nullable = true) - private AppUser rejectedBy; + protected AppUser rejectedBy; @Temporal(TemporalType.DATE) @Column(name = "withdrawnon_date") - private Date withdrawnOnDate; + protected Date withdrawnOnDate; @ManyToOne(optional = true) @JoinColumn(name = "withdrawnon_userid", nullable = true) - private AppUser withdrawnBy; + protected AppUser withdrawnBy; @Temporal(TemporalType.DATE) @Column(name = "approvedon_date") - private Date approvedOnDate; + protected Date approvedOnDate; @ManyToOne(optional = true) @JoinColumn(name = "approvedon_userid", nullable = true) - private AppUser approvedBy; + protected AppUser approvedBy; @Temporal(TemporalType.DATE) @Column(name = "activatedon_date", nullable = true) - private Date activatedOnDate; + protected Date activatedOnDate; @ManyToOne(optional = true) @JoinColumn(name = "activatedon_userid", nullable = true) - private AppUser activatedBy; + protected AppUser activatedBy; @Temporal(TemporalType.DATE) @Column(name = "closedon_date") - private Date closedOnDate; + protected Date closedOnDate; @ManyToOne(optional = true) @JoinColumn(name = "closedon_userid", nullable = true) - private AppUser closedBy; + protected AppUser closedBy; @Embedded - private MonetaryCurrency currency; + protected MonetaryCurrency currency; @Column(name = "nominal_annual_interest_rate", scale = 6, precision = 19, nullable = false) - private BigDecimal nominalAnnualInterestRate; + protected BigDecimal nominalAnnualInterestRate; /** * The interest period is the span of time at the end of which savings in a @@ -180,35 +189,35 @@ public class SavingsAccount extends AbstractPersistable { * enumeration. */ @Column(name = "interest_compounding_period_enum", nullable = false) - private Integer interestCompoundingPeriodType; + protected Integer interestCompoundingPeriodType; /** * A value from the {@link SavingsPostingInterestPeriodType} enumeration. */ @Column(name = "interest_posting_period_enum", nullable = false) - private Integer interestPostingPeriodType; + protected Integer interestPostingPeriodType; /** * A value from the {@link SavingsInterestCalculationType} enumeration. */ @Column(name = "interest_calculation_type_enum", nullable = false) - private Integer interestCalculationType; + protected Integer interestCalculationType; /** * A value from the {@link SavingsInterestCalculationDaysInYearType} * enumeration. */ @Column(name = "interest_calculation_days_in_year_type_enum", nullable = false) - private Integer interestCalculationDaysInYearType; + protected Integer interestCalculationDaysInYearType; @Column(name = "min_required_opening_balance", scale = 6, precision = 19, nullable = true) - private BigDecimal minRequiredOpeningBalance; + protected BigDecimal minRequiredOpeningBalance; @Column(name = "lockin_period_frequency", nullable = true) - private Integer lockinPeriodFrequency; + protected Integer lockinPeriodFrequency; @Column(name = "lockin_period_frequency_enum", nullable = true) - private Integer lockinPeriodFrequencyType; + protected Integer lockinPeriodFrequencyType; /** * When account becomes active this field is derived if @@ -217,10 +226,10 @@ public class SavingsAccount extends AbstractPersistable { */ @Temporal(TemporalType.DATE) @Column(name = "lockedin_until_date_derived", nullable = true) - private Date lockedInUntilDate; + protected Date lockedInUntilDate; @Column(name = "withdrawal_fee_for_transfer", nullable = true) - private boolean withdrawalFeeApplicableForTransfer; + protected boolean withdrawalFeeApplicableForTransfer; @Column(name = "allow_overdraft") private boolean allowOverdraft; @@ -229,23 +238,26 @@ public class SavingsAccount extends AbstractPersistable { private BigDecimal overdraftLimit; @Embedded - private SavingsAccountSummary summary; + protected SavingsAccountSummary summary; @OrderBy(value = "dateOf, createdDate, id") @LazyCollection(LazyCollectionOption.FALSE) @OneToMany(cascade = CascadeType.ALL, mappedBy = "savingsAccount", orphanRemoval = true) - private final List transactions = new ArrayList(); + protected final List transactions = new ArrayList(); @LazyCollection(LazyCollectionOption.FALSE) @OneToMany(cascade = CascadeType.ALL, mappedBy = "savingsAccount", orphanRemoval = true) - private Set charges = new HashSet(); + protected Set charges = new HashSet(); @Transient - private boolean accountNumberRequiresAutoGeneration = false; + protected boolean accountNumberRequiresAutoGeneration = false; @Transient - private SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper; + protected SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper; @Transient - private SavingsHelper savingsHelper; + protected SavingsHelper savingsHelper; + + @Column(name = "deposit_type_enum", insertable = false, updatable = false) + private Integer depositType; protected SavingsAccount() { // @@ -268,7 +280,7 @@ public class SavingsAccount extends AbstractPersistable { withdrawalFeeApplicableForTransfer, savingsAccountCharges, allowOverdraft, overdraftLimit); } - private SavingsAccount(final Client client, final Group group, final SavingsProduct product, final Staff fieldOfficer, + protected SavingsAccount(final Client client, final Group group, final SavingsProduct product, final Staff fieldOfficer, final String accountNo, final String externalId, final SavingsAccountStatusType status, final AccountType accountType, final LocalDate submittedOnDate, final AppUser submittedBy, final BigDecimal nominalAnnualInterestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, @@ -394,13 +406,13 @@ public class SavingsAccount extends AbstractPersistable { // update existing transactions so derived balance fields are // correct. - recalculateDailyBalances(openingAccountBalance); + recalculateDailyBalances(openingAccountBalance, interestPostingUpToDate); } this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); } - private SavingsAccountTransaction findInterestPostingTransactionFor(final LocalDate postingDate) { + protected SavingsAccountTransaction findInterestPostingTransactionFor(final LocalDate postingDate) { SavingsAccountTransaction postingTransation = null; @@ -436,7 +448,7 @@ public class SavingsAccount extends AbstractPersistable { // update existing transactions so derived balance fields are // correct. - recalculateDailyBalances(openingAccountBalance); + recalculateDailyBalances(openingAccountBalance, upToInterestCalculationDate); // 1. default to calculate interest based on entire history OR // 2. determine latest 'posting period' and find interest credited to @@ -459,12 +471,12 @@ public class SavingsAccount extends AbstractPersistable { Money periodStartingBalance = Money.zero(this.currency); final SavingsInterestCalculationType interestCalculationType = SavingsInterestCalculationType.fromInt(this.interestCalculationType); - final BigDecimal interestRateAsFraction = this.nominalAnnualInterestRate.divide(BigDecimal.valueOf(100l), mc); + final BigDecimal interestRateAsFraction = getEffectiveInterestRateAsFraction(mc, upToInterestCalculationDate); for (final LocalDateInterval periodInterval : postingPeriodIntervals) { final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance, - retreiveOrderedListOfTransactions(), this.currency, compoundingPeriodType, interestCalculationType, - interestRateAsFraction, daysInYearType.getValue()); + retreiveOrderedNonInterestPostingTransactions(), this.currency, compoundingPeriodType, interestCalculationType, + interestRateAsFraction, daysInYearType.getValue(), upToInterestCalculationDate); periodStartingBalance = postingPeriod.closingBalance(); @@ -479,7 +491,12 @@ public class SavingsAccount extends AbstractPersistable { return allPostingPeriods; } - private List retreiveOrderedListOfTransactions() { + @SuppressWarnings("unused") + protected BigDecimal getEffectiveInterestRateAsFraction(final MathContext mc, final LocalDate upToInterestCalculationDate) { + return this.nominalAnnualInterestRate.divide(BigDecimal.valueOf(100l), mc); + } + + protected List retreiveOrderedNonInterestPostingTransactions() { final List listOfTransactionsSorted = retreiveListOfTransactions(); final List orderedNonInterestPostingTransactions = new ArrayList(); @@ -493,7 +510,7 @@ public class SavingsAccount extends AbstractPersistable { return orderedNonInterestPostingTransactions; } - private List retreiveListOfTransactions() { + protected List retreiveListOfTransactions() { final List listOfTransactionsSorted = new ArrayList(); listOfTransactionsSorted.addAll(this.transactions); @@ -502,7 +519,7 @@ public class SavingsAccount extends AbstractPersistable { return listOfTransactionsSorted; } - private void recalculateDailyBalances(final Money openingAccountBalance) { + protected void recalculateDailyBalances(final Money openingAccountBalance, final LocalDate interestPostingUpToDate) { Money runningBalance = openingAccountBalance.copy(); @@ -556,7 +573,7 @@ public class SavingsAccount extends AbstractPersistable { accountTransactionsSorted = retreiveListOfTransactions(); } // loop over transactions in reverse - LocalDate endOfBalanceDate = DateUtils.getLocalDateOfTenant(); + LocalDate endOfBalanceDate = interestPostingUpToDate; for (int i = accountTransactionsSorted.size() - 1; i >= 0; i--) { final SavingsAccountTransaction transaction = accountTransactionsSorted.get(i); if (transaction.isNotReversed()) { @@ -796,21 +813,26 @@ public class SavingsAccount extends AbstractPersistable { return transactionDate.isAfter(DateUtils.getLocalDateOfTenant()); } - private BigDecimal getAccountBalance() { + protected BigDecimal getAccountBalance() { return this.summary.getAccountBalance(this.currency).getAmount(); } public void modifyApplication(final JsonCommand command, final Map actualChanges) { - final List dataValidationErrors = new ArrayList(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource(SAVINGS_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.modifyApplicationAction); + this.modifyApplication(command, actualChanges, baseDataValidator); + + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + public void modifyApplication(final JsonCommand command, final Map actualChanges, + final DataValidatorBuilder baseDataValidator) { final SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); if (!SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.hasStateOf(currentStatus)) { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("not.in.submittedandpendingapproval.state"); - - if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + return; } final String localeAsInput = command.locale(); @@ -954,15 +976,17 @@ public class SavingsAccount extends AbstractPersistable { this.overdraftLimit = null; } - validateLockinDetails(); - + validateLockinDetails(baseDataValidator); } - private void validateLockinDetails() { + private void validateLockinDetails(final DataValidatorBuilder baseDataValidator) { - final List dataValidationErrors = new ArrayList(); - final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) - .resource(SAVINGS_ACCOUNT_RESOURCE_NAME); + /* + * final List dataValidationErrors = new + * ArrayList(); final DataValidatorBuilder + * baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + * .resource(resourceName); + */ if (this.lockinPeriodFrequency == null) { baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(this.lockinPeriodFrequencyType).ignoreIfNull() @@ -978,8 +1002,6 @@ public class SavingsAccount extends AbstractPersistable { baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(this.lockinPeriodFrequencyType).notNull() .inMinMaxRange(0, 3); } - - if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } public Map deriveAccountingBridgeData(final CurrencyData currencyData, final Set existingTransactionIds, @@ -1115,17 +1137,19 @@ public class SavingsAccount extends AbstractPersistable { return this.currency; } - public void validateNewApplicationState(final LocalDate todayDateOfTenant) { + public void validateNewApplicationState(final LocalDate todayDateOfTenant, final String resourceName) { - validateLockinDetails(); // validateWithdrawalFeeDetails(); // validateAnnualFeeDetails(); final LocalDate submittedOn = getSubmittedOnLocalDate(); final List dataValidationErrors = new ArrayList(); - final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) - .resource(SAVINGS_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.summitalAction); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(resourceName + + SavingsApiConstants.summitalAction); + + validateLockinDetails(baseDataValidator); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } if (submittedOn.isAfter(todayDateOfTenant)) { baseDataValidator.reset().parameter(SavingsApiConstants.submittedOnDateParamName).value(submittedOn) @@ -1144,7 +1168,7 @@ public class SavingsAccount extends AbstractPersistable { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - private LocalDate getSubmittedOnLocalDate() { + protected LocalDate getSubmittedOnLocalDate() { LocalDate submittedOn = null; if (this.submittedOnDate != null) { submittedOn = new LocalDate(this.submittedOnDate); @@ -1301,7 +1325,7 @@ public class SavingsAccount extends AbstractPersistable { Date nextDueDate = null; LocalDate lastAnnualFeeTransactionDate = null; - for (final SavingsAccountTransaction transaction : retreiveOrderedListOfTransactions()) { + for (final SavingsAccountTransaction transaction : retreiveOrderedNonInterestPostingTransactions()) { if (transaction.isAnnualFeeAndNotReversed()) { if (lastAnnualFeeTransactionDate == null) { lastAnnualFeeTransactionDate = transaction.transactionLocalDate(); @@ -1522,7 +1546,7 @@ public class SavingsAccount extends AbstractPersistable { return actualChanges; } - private void processAccountUponActivation(final DateTimeFormatter fmt) { + protected void processAccountUponActivation(final DateTimeFormatter fmt) { final Money minRequiredOpeningBalance = Money.of(this.currency, this.minRequiredOpeningBalance); if (minRequiredOpeningBalance.isGreaterThanZero()) { final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, getActivationLocalDate(), @@ -1536,7 +1560,7 @@ public class SavingsAccount extends AbstractPersistable { // update existing transactions so derived balance fields are // correct. - recalculateDailyBalances(openingAccountBalance); + recalculateDailyBalances(openingAccountBalance, DateUtils.getLocalDateOfTenant()); } // update annual fee due date @@ -1631,7 +1655,7 @@ public class SavingsAccount extends AbstractPersistable { return actualChanges; } - private void validateActivityNotBeforeClientOrGroupTransferDate(final SavingsEvent event, final LocalDate activityDate) { + protected void validateActivityNotBeforeClientOrGroupTransferDate(final SavingsEvent event, final LocalDate activityDate) { if (this.client != null && this.client.getOfficeJoiningLocalDate() != null) { final LocalDate clientOfficeJoiningDate = this.client.getOfficeJoiningLocalDate(); if (activityDate.isBefore(clientOfficeJoiningDate)) { throw new SavingsActivityPriorToClientTransferException(event.toString(), @@ -2005,8 +2029,15 @@ public class SavingsAccount extends AbstractPersistable { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - public boolean allowOverdraft() { - return this.allowOverdraft; - } + public boolean allowOverdraft() { + return this.allowOverdraft; + } + public LocalDate accountSubmittedOrActivationDate() { + return getActivationLocalDate() == null ? getSubmittedOnLocalDate() : getActivationLocalDate(); + } + + public DepositAccountType depositAccountType() { + return DepositAccountType.fromInt(depositType); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java index 4cb252e30..ee48b48f4 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java @@ -6,6 +6,7 @@ package org.mifosplatform.portfolio.savings.domain; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountNoParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.allowOverdraftParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.clientIdParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.externalIdParamName; @@ -217,7 +218,7 @@ public class SavingsAccountAssembler { overdraftLimit); account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper); - account.validateNewApplicationState(DateUtils.getLocalDateOfTenant()); + account.validateNewApplicationState(DateUtils.getLocalDateOfTenant(), SAVINGS_ACCOUNT_RESOURCE_NAME); account.validateAccountValuesWithProduct(); @@ -270,7 +271,7 @@ public class SavingsAccountAssembler { product.isWithdrawalFeeApplicableForTransfer(), charges, product.isAllowOverdraft(), product.overdraftLimit()); account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper); - account.validateNewApplicationState(DateUtils.getLocalDateOfTenant()); + account.validateNewApplicationState(DateUtils.getLocalDateOfTenant(), SAVINGS_ACCOUNT_RESOURCE_NAME); account.validateAccountValuesWithProduct(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountRepository.java index c716d5abd..96451c35e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountRepository.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountRepository.java @@ -28,4 +28,7 @@ public interface SavingsAccountRepository extends JpaRepository findByGroupId(@Param("groupId") Long groupId); + + @Query("from SavingsAccount sa where sa.id = :accountId and sa.depositType = :depositAccountTypeId") + SavingsAccount findByIdAndDepositAccountType(@Param("accountId") Long accountId, @Param("depositAccountTypeId") Integer depositAccountTypeId); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountRepositoryWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountRepositoryWrapper.java index c7d651f8d..d1fb398ff 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountRepositoryWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountRepositoryWrapper.java @@ -5,6 +5,7 @@ */ package org.mifosplatform.portfolio.savings.domain; +import org.mifosplatform.portfolio.savings.DepositAccountType; import org.mifosplatform.portfolio.savings.exception.SavingsAccountNotFoundException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -36,6 +37,12 @@ public class SavingsAccountRepositoryWrapper { if (account == null) { throw new SavingsAccountNotFoundException(savingsId); } return account; } + + public SavingsAccount findOneWithNotFoundDetection(final Long savingsId, final DepositAccountType depositAccountType) { + final SavingsAccount account = this.repository.findByIdAndDepositAccountType(savingsId, depositAccountType.getValue()); + if (account == null) { throw new SavingsAccountNotFoundException(savingsId); } + return account; + } public void save(final SavingsAccount account) { this.repository.save(account); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountStatusType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountStatusType.java index e28457092..5e76c77eb 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountStatusType.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountStatusType.java @@ -18,7 +18,10 @@ public enum SavingsAccountStatusType { TRANSFER_ON_HOLD(304, "savingsAccountStatusType.transfer.on.hold"), // WITHDRAWN_BY_APPLICANT(400, "savingsAccountStatusType.withdrawn.by.applicant"), // REJECTED(500, "savingsAccountStatusType.rejected"), // - CLOSED(600, "savingsAccountStatusType.closed"); + CLOSED(600, "savingsAccountStatusType.closed"), + PRE_MATURE_CLOSURE(700, "savingsAccountStatusType.pre.mature.closure"), + MATURED(800, "savingsAccountStatusType.matured"); + private final Integer value; private final String code; @@ -51,6 +54,12 @@ public enum SavingsAccountStatusType { case 600: enumeration = SavingsAccountStatusType.CLOSED; break; + case 700: + enumeration = SavingsAccountStatusType.PRE_MATURE_CLOSURE; + break; + case 800: + enumeration = SavingsAccountStatusType.MATURED; + break; } return enumeration; } @@ -107,4 +116,12 @@ public enum SavingsAccountStatusType { public boolean isUnderTransfer() { return isTransferInProgress() || isTransferOnHold(); } + + public boolean isMatured() { + return this.value.equals(SavingsAccountStatusType.MATURED.getValue()); + } + + public boolean isPreMatureClosure() { + return this.value.equals(SavingsAccountStatusType.PRE_MATURE_CLOSURE.getValue()); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountSummary.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountSummary.java index 3ca03edf4..7445eccc5 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountSummary.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountSummary.java @@ -84,7 +84,9 @@ public final class SavingsAccountSummary { Money totalEarned = Money.zero(currency); for (final PostingPeriod period : allPostingPeriods) { - totalEarned = totalEarned.plus(period.interest()); + Money interestEarned = period.interest(); + interestEarned = interestEarned == null ? Money.zero(currency) : interestEarned; + totalEarned = totalEarned.plus(interestEarned); } this.totalInterestEarned = totalEarned.getAmount(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsHelper.java index 836f04ce1..a0b1df57f 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsHelper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsHelper.java @@ -6,7 +6,6 @@ import java.util.List; import org.joda.time.DateTime; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.domain.LocalDateInterval; -import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType; @@ -27,10 +26,10 @@ public final class SavingsHelper { while (!periodStartDate.isAfter(interestPostingUpToDate) && !periodEndDate.isAfter(interestPostingUpToDate)) { - final LocalDate interestPostingLocalDate = determineInterestPostingPeriodEndDateFrom(periodStartDate, postingPeriodType); + final LocalDate interestPostingLocalDate = determineInterestPostingPeriodEndDateFrom(periodStartDate, postingPeriodType, interestPostingUpToDate); periodEndDate = interestPostingLocalDate.minusDays(1); - if (!interestPostingLocalDate.isAfter(DateUtils.getLocalDateOfTenant())) { + if (!interestPostingLocalDate.isAfter(interestPostingUpToDate)) { postingPeriods.add(LocalDateInterval.create(periodStartDate, periodEndDate)); } else { postingPeriods.add(LocalDateInterval.create(periodStartDate, periodEndDate)); @@ -44,9 +43,9 @@ public final class SavingsHelper { } private LocalDate determineInterestPostingPeriodEndDateFrom(final LocalDate periodStartDate, - final SavingsPostingInterestPeriodType interestPostingPeriodType) { + final SavingsPostingInterestPeriodType interestPostingPeriodType, final LocalDate interestPostingUpToDate) { - LocalDate periodEndDate = DateUtils.getLocalDateOfTenant(); + LocalDate periodEndDate = interestPostingUpToDate; switch (interestPostingPeriodType) { case INVALID: @@ -62,7 +61,7 @@ public final class SavingsHelper { final int monthofYear = periodStartDate.getMonthOfYear(); if (monthofYear <= 3) { periodEndDate = new DateTime().withDate(year, 3, 31).toLocalDate(); - } else if (monthofYear <= 6) { + } else if (monthofYear <= 6) { periodEndDate = new DateTime().withDate(year, 6, 30).toLocalDate(); } else if (monthofYear <= 9) { periodEndDate = new DateTime().withDate(year, 9, 30).toLocalDate(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java index a704401cc..70ff25412 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java @@ -36,14 +36,19 @@ import java.util.Map; import java.util.Set; import javax.persistence.Column; +import javax.persistence.DiscriminatorColumn; +import javax.persistence.DiscriminatorType; +import javax.persistence.DiscriminatorValue; import javax.persistence.Embedded; import javax.persistence.Entity; +import javax.persistence.Inheritance; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; +import org.joda.time.LocalDate; import org.mifosplatform.accounting.common.AccountingRuleType; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.ApiParameterError; @@ -52,6 +57,7 @@ import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidation import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.portfolio.charge.domain.Charge; +import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChart; import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType; import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; @@ -63,23 +69,26 @@ import com.google.gson.JsonArray; @Entity @Table(name = "m_savings_product", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "sp_unq_name"), - @UniqueConstraint(columnNames = { "short_name" }, name = "sp_unq_short_name") }) + @UniqueConstraint(columnNames = { "short_name" }, name = "sp_unq_short_name")}) +@Inheritance +@DiscriminatorColumn(name="deposit_type_enum", discriminatorType=DiscriminatorType.INTEGER) +@DiscriminatorValue("100") public class SavingsProduct extends AbstractPersistable { @Column(name = "name", nullable = false, unique = true) - private String name; + protected String name; @Column(name = "short_name", nullable = false, unique = true) - private String shortName; + protected String shortName; @Column(name = "description", length = 500, nullable = false) - private String description; + protected String description; @Embedded - private MonetaryCurrency currency; + protected MonetaryCurrency currency; @Column(name = "nominal_annual_interest_rate", scale = 6, precision = 19, nullable = false) - private BigDecimal nominalAnnualInterestRate; + protected BigDecimal nominalAnnualInterestRate; /** * The interest period is the span of time at the end of which savings in a @@ -89,48 +98,48 @@ public class SavingsProduct extends AbstractPersistable { * enumeration. */ @Column(name = "interest_compounding_period_enum", nullable = false) - private Integer interestCompoundingPeriodType; + protected Integer interestCompoundingPeriodType; /** * A value from the {@link SavingsPostingInterestPeriodType} enumeration. */ @Column(name = "interest_posting_period_enum", nullable = false) - private Integer interestPostingPeriodType; + protected Integer interestPostingPeriodType; /** * A value from the {@link SavingsInterestCalculationType} enumeration. */ @Column(name = "interest_calculation_type_enum", nullable = false) - private Integer interestCalculationType; + protected Integer interestCalculationType; /** * A value from the {@link SavingsInterestCalculationDaysInYearType} * enumeration. */ @Column(name = "interest_calculation_days_in_year_type_enum", nullable = false) - private Integer interestCalculationDaysInYearType; + protected Integer interestCalculationDaysInYearType; @Column(name = "min_required_opening_balance", scale = 6, precision = 19, nullable = true) - private BigDecimal minRequiredOpeningBalance; + protected BigDecimal minRequiredOpeningBalance; @Column(name = "lockin_period_frequency", nullable = true) - private Integer lockinPeriodFrequency; + protected Integer lockinPeriodFrequency; @Column(name = "lockin_period_frequency_enum", nullable = true) - private Integer lockinPeriodFrequencyType; + protected Integer lockinPeriodFrequencyType; /** * A value from the {@link AccountingRuleType} enumeration. */ @Column(name = "accounting_type", nullable = false) - private Integer accountingRule; + protected Integer accountingRule; @Column(name = "withdrawal_fee_for_transfer") - private boolean withdrawalFeeApplicableForTransfer; + protected boolean withdrawalFeeApplicableForTransfer; @ManyToMany @JoinTable(name = "m_savings_product_charge", joinColumns = @JoinColumn(name = "savings_product_id"), inverseJoinColumns = @JoinColumn(name = "charge_id")) - private Set charges; + protected Set charges; @Column(name = "allow_overdraft") private boolean allowOverdraft; @@ -158,8 +167,8 @@ public class SavingsProduct extends AbstractPersistable { this.description = null; } - private SavingsProduct(final String name, final String shortName, final String description, final MonetaryCurrency currency, - final BigDecimal interestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, + protected SavingsProduct(final String name, final String shortName, final String description, final MonetaryCurrency currency, final BigDecimal interestRate, + final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, @@ -187,12 +196,6 @@ public class SavingsProduct extends AbstractPersistable { } this.withdrawalFeeApplicableForTransfer = withdrawalFeeApplicableForTransfer; - /* - * this.annualFeeAmount = annualFeeAmount; if (annualFeeAmount != null - * && annualFeeOnMonthDay != null) { this.annualFeeOnMonth = - * annualFeeOnMonthDay.getMonthOfYear(); this.annualFeeOnDay = - * annualFeeOnMonthDay.getDayOfMonth(); } - */ if (accountingRuleType != null) { this.accountingRule = accountingRuleType.getValue(); @@ -203,8 +206,6 @@ public class SavingsProduct extends AbstractPersistable { } validateLockinDetails(); - // validateWithdrawalFeeDetails(); - // validateAnnualFeeDetails(); this.allowOverdraft = allowOverdraft; this.overdraftLimit = overdraftLimit; } @@ -356,71 +357,13 @@ public class SavingsProduct extends AbstractPersistable { if (this.lockinPeriodFrequency == null) { this.lockinPeriodFrequencyType = null; } - /* - * if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull( - * withdrawalFeeAmountParamName, this.withdrawalFeeAmount)) { final - * BigDecimal newValue = - * command.bigDecimalValueOfParameterNamedDefaultToNullIfZero - * (withdrawalFeeAmountParamName); - * actualChanges.put(withdrawalFeeAmountParamName, newValue); - * actualChanges.put(localeParamName, localeAsInput); - * this.withdrawalFeeAmount = newValue; } - * - * if (command.isChangeInIntegerParameterNamedDefaultingZeroToNull( - * withdrawalFeeTypeParamName, this.withdrawalFeeType)) { final Integer - * newValue = command.integerValueOfParameterNamedDefaultToNullIfZero( - * withdrawalFeeTypeParamName); - * actualChanges.put(withdrawalFeeTypeParamName, newValue); - * this.withdrawalFeeType = newValue != null ? - * SavingsWithdrawalFeesType.fromInt(newValue).getValue() : newValue; } - */ + if (command.isChangeInBooleanParameterNamed(withdrawalFeeForTransfersParamName, this.withdrawalFeeApplicableForTransfer)) { final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(withdrawalFeeForTransfersParamName); actualChanges.put(withdrawalFeeForTransfersParamName, newValue); this.withdrawalFeeApplicableForTransfer = newValue; } - // set period type to null if frequency is null - // if (this.withdrawalFeeAmount == null) { - // this.withdrawalFeeType = null; - // } - - /* - * if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull( - * feeAmountParamName, this.annualFeeAmount)) { final BigDecimal - * newValue = - * command.bigDecimalValueOfParameterNamedDefaultToNullIfZero( - * feeAmountParamName); actualChanges.put(feeAmountParamName, newValue); - * actualChanges.put(localeParamName, localeAsInput); - * this.annualFeeAmount = newValue; } - * - * if (command.isChangeInIntegerParameterNamedDefaultingZeroToNull( - * feeOnMonthDayParamName, this.annualFeeOnDay)) { final MonthDay - * monthDay = command.extractMonthDayNamed(feeOnMonthDayParamName); - * final String actualValueEntered = - * command.stringValueOfParameterNamed(feeOnMonthDayParamName); final - * Integer newValue = monthDay != null ? monthDay.getDayOfMonth() : - * null; actualChanges.put(feeOnMonthDayParamName, actualValueEntered); - * actualChanges.put(localeParamName, localeAsInput); - * this.annualFeeOnDay = newValue; } - * - * if (command.isChangeInIntegerParameterNamedDefaultingZeroToNull( - * feeOnMonthDayParamName, this.annualFeeOnMonth)) { final MonthDay - * monthDay = command.extractMonthDayNamed(feeOnMonthDayParamName); - * final String actualValueEntered = - * command.stringValueOfParameterNamed(feeOnMonthDayParamName); final - * Integer newValue = monthDay != null ? monthDay.getMonthOfYear() : - * null; actualChanges.put(feeOnMonthDayParamName, actualValueEntered); - * actualChanges.put(localeParamName, localeAsInput); - * this.annualFeeOnMonth = newValue; } - */ - - // // set period type to null if frequency is null - // if (this.annualFeeAmount == null) { - // this.annualFeeOnDay = null; - // this.annualFeeOnMonth = null; - // } - if (command.isChangeInIntegerParameterNamed(accountingRuleParamName, this.accountingRule)) { final Integer newValue = command.integerValueOfParameterNamed(accountingRuleParamName); actualChanges.put(accountingRuleParamName, newValue); @@ -452,64 +395,10 @@ public class SavingsProduct extends AbstractPersistable { } validateLockinDetails(); - // validateWithdrawalFeeDetails(); - // validateAnnualFeeDetails(); return actualChanges; } - /* - * private void validateAnnualFeeDetails() { - * - * final List dataValidationErrors = new - * ArrayList(); final DataValidatorBuilder - * baseDataValidator = new DataValidatorBuilder(dataValidationErrors) - * .resource(SAVINGS_PRODUCT_RESOURCE_NAME); - * - * if (this.annualFeeAmount == null) { - * - * if (this.annualFeeOnMonth != null || this.annualFeeOnDay != null) { - * baseDataValidator - * .reset().parameter(feeAmountParamName).value(this.annualFeeAmount - * ).notNull(); } } else { - * - * if (this.annualFeeOnMonth == null || this.annualFeeOnDay == null) { - * baseDataValidator - * .reset().parameter(feeOnMonthDayParamName).value(this.annualFeeOnMonth - * ).notNull(); } - * - * baseDataValidator.reset().parameter(feeAmountParamName).value(this. - * annualFeeAmount).zeroOrPositiveAmount(); } - * - * if (!dataValidationErrors.isEmpty()) { throw new - * PlatformApiDataValidationException(dataValidationErrors); } } - */ - /* - * private void validateWithdrawalFeeDetails() { - * - * final List dataValidationErrors = new - * ArrayList(); final DataValidatorBuilder - * baseDataValidator = new DataValidatorBuilder(dataValidationErrors) - * .resource(SAVINGS_PRODUCT_RESOURCE_NAME); - * - * if (this.withdrawalFeeAmount == null) { - * baseDataValidator.reset().parameter - * (withdrawalFeeTypeParamName).value(this.withdrawalFeeType).ignoreIfNull() - * .isOneOfTheseValues(1, 2); - * - * if (this.withdrawalFeeType != null) { - * baseDataValidator.reset().parameter( - * withdrawalFeeAmountParamName).value(this.withdrawalFeeAmount).notNull(); - * } } else { - * baseDataValidator.reset().parameter(withdrawalFeeAmountParamName - * ).value(this.withdrawalFeeAmount).zeroOrPositiveAmount(); - * baseDataValidator - * .reset().parameter(withdrawalFeeTypeParamName).value(this - * .withdrawalFeeType).notNull() .isOneOfTheseValues(1, 2); } - * - * if (!dataValidationErrors.isEmpty()) { throw new - * PlatformApiDataValidationException(dataValidationErrors); } } - */ private void validateLockinDetails() { final List dataValidationErrors = new ArrayList(); @@ -590,4 +479,13 @@ public class SavingsProduct extends AbstractPersistable { public Set charges() { return this.charges; } + + public InterestRateChart applicableChart(@SuppressWarnings("unused") final LocalDate target){ + return null; + } + + public InterestRateChart findChart(@SuppressWarnings("unused") Long chartId) { + return null; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/DailyCompoundingPeriod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/DailyCompoundingPeriod.java index 3d29c4b9d..4052ea8fe 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/DailyCompoundingPeriod.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/DailyCompoundingPeriod.java @@ -5,8 +5,8 @@ import java.math.MathContext; import java.util.ArrayList; import java.util.List; +import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.domain.LocalDateInterval; -import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; @@ -16,15 +16,15 @@ public class DailyCompoundingPeriod implements CompoundingPeriod { private final LocalDateInterval periodInterval; private final List endOfDayBalances; - public static DailyCompoundingPeriod create(final LocalDateInterval periodInterval, final List allEndOfDayBalances) { + public static DailyCompoundingPeriod create(final LocalDateInterval periodInterval, final List allEndOfDayBalances, final LocalDate upToInterestCalculationDate) { - final List endOfDayBalancesWithinPeriod = endOfDayBalancesWithinPeriodInterval(periodInterval, allEndOfDayBalances); + final List endOfDayBalancesWithinPeriod = endOfDayBalancesWithinPeriodInterval(periodInterval, allEndOfDayBalances, upToInterestCalculationDate); return new DailyCompoundingPeriod(periodInterval, endOfDayBalancesWithinPeriod); } private static List endOfDayBalancesWithinPeriodInterval(final LocalDateInterval compoundingPeriodInterval, - final List allEndOfDayBalances) { + final List allEndOfDayBalances, final LocalDate upToInterestCalculationDate) { final List endOfDayBalancesForPeriodInterval = new ArrayList(); @@ -33,13 +33,13 @@ public class DailyCompoundingPeriod implements CompoundingPeriod { for (final EndOfDayBalance endOfDayBalance : allEndOfDayBalances) { if (compoundingPeriodInterval.contains(endOfDayBalance.date())) { - cappedToPeriodEndDate = endOfDayBalance.upTo(compoundingPeriodInterval); + cappedToPeriodEndDate = endOfDayBalance.upTo(compoundingPeriodInterval, upToInterestCalculationDate); } else if (endOfDayBalance.contains(compoundingPeriodInterval)) { - cappedToPeriodEndDate = endOfDayBalance.upTo(compoundingPeriodInterval); + cappedToPeriodEndDate = endOfDayBalance.upTo(compoundingPeriodInterval, upToInterestCalculationDate); } else { final LocalDateInterval latestPeriod = LocalDateInterval.create(compoundingPeriodInterval.startDate(), - DateUtils.getLocalDateOfTenant()); - cappedToPeriodEndDate = endOfDayBalance.upTo(latestPeriod); + upToInterestCalculationDate); + cappedToPeriodEndDate = endOfDayBalance.upTo(latestPeriod, upToInterestCalculationDate); } if (cappedToPeriodEndDate != null) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/EndOfDayBalance.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/EndOfDayBalance.java index 095d523f1..b6d57f6d2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/EndOfDayBalance.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/EndOfDayBalance.java @@ -6,7 +6,6 @@ import java.math.RoundingMode; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.domain.LocalDateInterval; -import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.organisation.monetary.domain.Money; public class EndOfDayBalance { @@ -78,7 +77,15 @@ public class EndOfDayBalance { return futureValue.subtract(presentValue); } - public EndOfDayBalance upTo(final LocalDateInterval compoundingPeriodInterval) { + /** + * @param compoundingPeriodInterval + * @param upToInterestCalculationDate + * : For calculating maturity details in advance upToInterestCalculationDate + * will be maturity date else it will be + * DateUtils.getLocalDateOfTenant(). + * @return + */ + public EndOfDayBalance upTo(final LocalDateInterval compoundingPeriodInterval, final LocalDate upToInterestCalculationDate) { Money startingBalance = this.openingBalance; LocalDate balanceStartDate = this.date; @@ -95,8 +102,8 @@ public class EndOfDayBalance { final LocalDateInterval balancePeriodInterval = LocalDateInterval.create(balanceStartDate, balanceEndDate); daysOfBalance = balancePeriodInterval.daysInPeriodInclusiveOfEndDate(); } - if (balanceEndDate.isAfter(DateUtils.getLocalDateOfTenant())) { - balanceEndDate = DateUtils.getLocalDateOfTenant(); + if (balanceEndDate.isAfter(upToInterestCalculationDate)) { + balanceEndDate = upToInterestCalculationDate; final LocalDateInterval balancePeriodInterval = LocalDateInterval.create(balanceStartDate, balanceEndDate); daysOfBalance = balancePeriodInterval.daysInPeriodInclusiveOfEndDate(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/MonthlyCompoundingPeriod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/MonthlyCompoundingPeriod.java index 5b1ddcb64..ee97e203a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/MonthlyCompoundingPeriod.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/MonthlyCompoundingPeriod.java @@ -6,6 +6,7 @@ import java.math.RoundingMode; import java.util.ArrayList; import java.util.List; +import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.domain.LocalDateInterval; import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; @@ -16,9 +17,9 @@ public class MonthlyCompoundingPeriod implements CompoundingPeriod { private final LocalDateInterval periodInterval; private final List endOfDayBalances; - public static MonthlyCompoundingPeriod create(final LocalDateInterval periodInterval, final List allEndOfDayBalances) { + public static MonthlyCompoundingPeriod create(final LocalDateInterval periodInterval, final List allEndOfDayBalances, final LocalDate upToInterestCalculationDate) { - final List endOfDayBalancesWithinPeriod = endOfDayBalancesWithinPeriodInterval(periodInterval, allEndOfDayBalances); + final List endOfDayBalancesWithinPeriod = endOfDayBalancesWithinPeriodInterval(periodInterval, allEndOfDayBalances, upToInterestCalculationDate); return new MonthlyCompoundingPeriod(periodInterval, endOfDayBalancesWithinPeriod); } @@ -125,17 +126,17 @@ public class MonthlyCompoundingPeriod implements CompoundingPeriod { } private static List endOfDayBalancesWithinPeriodInterval(final LocalDateInterval compoundingPeriodInterval, - final List allEndOfDayBalances) { + final List allEndOfDayBalances, final LocalDate upToInterestCalculationDate) { final List endOfDayBalancesForPeriodInterval = new ArrayList(); for (final EndOfDayBalance endOfDayBalance : allEndOfDayBalances) { if (compoundingPeriodInterval.contains(endOfDayBalance.date())) { - final EndOfDayBalance cappedToPeriodEndDate = endOfDayBalance.upTo(compoundingPeriodInterval); + final EndOfDayBalance cappedToPeriodEndDate = endOfDayBalance.upTo(compoundingPeriodInterval, upToInterestCalculationDate); endOfDayBalancesForPeriodInterval.add(cappedToPeriodEndDate); } else if (endOfDayBalance.contains(compoundingPeriodInterval)) { - final EndOfDayBalance cappedToPeriodEndDate = endOfDayBalance.upTo(compoundingPeriodInterval); + final EndOfDayBalance cappedToPeriodEndDate = endOfDayBalance.upTo(compoundingPeriodInterval, upToInterestCalculationDate); endOfDayBalancesForPeriodInterval.add(cappedToPeriodEndDate); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/PostingPeriod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/PostingPeriod.java index 1f0f28779..63d72f4ce 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/PostingPeriod.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/PostingPeriod.java @@ -6,7 +6,6 @@ import java.util.List; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.domain.LocalDateInterval; -import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; @@ -29,7 +28,6 @@ public class PostingPeriod { private Money interestEarnedRounded; // opening/closing details - @SuppressWarnings("unused") private final Money openingBalance; private final Money closingBalance; private final SavingsInterestCalculationType interestCalculationType; @@ -37,7 +35,7 @@ public class PostingPeriod { public static PostingPeriod createFrom(final LocalDateInterval periodInterval, final Money periodStartingBalance, final List orderedListOfTransactions, final MonetaryCurrency currency, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, - final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestRateAsFraction, final long daysInYear) { + final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestRateAsFraction, final long daysInYear, final LocalDate upToInterestCalculationDate) { final List accountEndOfDayBalances = new ArrayList(); @@ -62,8 +60,19 @@ public class PostingPeriod { } if (accountEndOfDayBalances.isEmpty()) { - final EndOfDayBalance endOfDayBalance = EndOfDayBalance.from(periodInterval.startDate(), openingDayBalance, closeOfDayBalance, - periodInterval.daysInPeriodInclusiveOfEndDate()); + LocalDate balanceStartDate = periodInterval.startDate(); + LocalDate balanceEndDate = periodInterval.endDate(); + Integer numberOfDaysOfBalance = periodInterval.daysInPeriodInclusiveOfEndDate(); + + if (balanceEndDate.isAfter(upToInterestCalculationDate)) { + balanceEndDate = upToInterestCalculationDate; + final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate); + numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate(); + } + + final EndOfDayBalance endOfDayBalance = EndOfDayBalance.from(balanceStartDate, openingDayBalance, closeOfDayBalance, + numberOfDaysOfBalance); + accountEndOfDayBalances.add(endOfDayBalance); closeOfDayBalance = endOfDayBalance.closingBalance(); @@ -71,7 +80,7 @@ public class PostingPeriod { } final List compoundingPeriods = compoundingPeriodsInPostingPeriod(periodInterval, interestCompoundingPeriodType, - accountEndOfDayBalances); + accountEndOfDayBalances, upToInterestCalculationDate); return new PostingPeriod(periodInterval, currency, periodStartingBalance, openingDayBalance, interestCompoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYear, compoundingPeriods); @@ -105,6 +114,10 @@ public class PostingPeriod { public Money closingBalance() { return this.closingBalance; } + + public Money openingBalance() { + return this.openingBalance; + } public BigDecimal calculateInterest(final BigDecimal interestFromPreviousPostingPeriod) { BigDecimal interestEarned = BigDecimal.ZERO; @@ -131,7 +144,7 @@ public class PostingPeriod { } private static List compoundingPeriodsInPostingPeriod(final LocalDateInterval postingPeriodInterval, - final SavingsCompoundingInterestPeriodType interestPeriodType, final List allEndOfDayBalances) { + final SavingsCompoundingInterestPeriodType interestPeriodType, final List allEndOfDayBalances, final LocalDate upToInterestCalculationDate) { final List compoundingPeriods = new ArrayList(); @@ -140,7 +153,7 @@ public class PostingPeriod { case INVALID: break; case DAILY: - compoundingPeriod = DailyCompoundingPeriod.create(postingPeriodInterval, allEndOfDayBalances); + compoundingPeriod = DailyCompoundingPeriod.create(postingPeriodInterval, allEndOfDayBalances, upToInterestCalculationDate); compoundingPeriods.add(compoundingPeriod); break; case MONTHLY: @@ -152,7 +165,7 @@ public class PostingPeriod { while (!periodStartDate.isAfter(postingPeriodEndDate) && !periodEndDate.isAfter(postingPeriodEndDate)) { - periodEndDate = determineInterestPeriodEndDateFrom(periodStartDate, interestPeriodType); + periodEndDate = determineInterestPeriodEndDateFrom(periodStartDate, interestPeriodType, upToInterestCalculationDate); if (periodEndDate.isAfter(postingPeriodEndDate)) { periodEndDate = postingPeriodEndDate; } @@ -160,7 +173,7 @@ public class PostingPeriod { final LocalDateInterval compoundingPeriodInterval = LocalDateInterval.create(periodStartDate, periodEndDate); if (postingPeriodInterval.contains(compoundingPeriodInterval)) { - compoundingPeriod = MonthlyCompoundingPeriod.create(compoundingPeriodInterval, allEndOfDayBalances); + compoundingPeriod = MonthlyCompoundingPeriod.create(compoundingPeriodInterval, allEndOfDayBalances, upToInterestCalculationDate); compoundingPeriods.add(compoundingPeriod); } @@ -186,9 +199,9 @@ public class PostingPeriod { } private static LocalDate determineInterestPeriodEndDateFrom(final LocalDate periodStartDate, - final SavingsCompoundingInterestPeriodType interestPeriodType) { + final SavingsCompoundingInterestPeriodType interestPeriodType, final LocalDate upToInterestCalculationDate) { - LocalDate periodEndDate = DateUtils.getLocalDateOfTenant(); + LocalDate periodEndDate = upToInterestCalculationDate; switch (interestPeriodType) { case INVALID: diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/DepositAccountInterestRateChartNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/DepositAccountInterestRateChartNotFoundException.java new file mode 100644 index 000000000..2fb76e321 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/DepositAccountInterestRateChartNotFoundException.java @@ -0,0 +1,15 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +public class DepositAccountInterestRateChartNotFoundException extends AbstractPlatformResourceNotFoundException { + + public DepositAccountInterestRateChartNotFoundException(final Long id) { + super("error.msg.deposit.account.interest.rate.chart.id.invalid", "Deposit Account Interest rate chart with identifier " + id + " does not exist", id); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/DepositAccountNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/DepositAccountNotFoundException.java new file mode 100644 index 000000000..c55ca5c98 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/DepositAccountNotFoundException.java @@ -0,0 +1,18 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.exception; + +import org.apache.commons.lang.StringUtils; +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; +import org.mifosplatform.portfolio.savings.DepositAccountType; + +public class DepositAccountNotFoundException extends AbstractPlatformResourceNotFoundException { + + public DepositAccountNotFoundException(final DepositAccountType accountType, final Long id) { + super("error.msg." + accountType.getCode().toLowerCase() + ".id.invalid", StringUtils.capitalize(accountType.toString() + .toLowerCase()) + " account with identifier " + id + " does not exist", id); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/DepositAccountTransactionNotAllowedException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/DepositAccountTransactionNotAllowedException.java new file mode 100644 index 000000000..2f76c40af --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/DepositAccountTransactionNotAllowedException.java @@ -0,0 +1,17 @@ +package org.mifosplatform.portfolio.savings.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformServiceUnavailableException; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.service.SavingsEnumerations; + +/** + * A {@link RuntimeException} thrown when deposit account transaction not + * allowed. + */ +public class DepositAccountTransactionNotAllowedException extends AbstractPlatformServiceUnavailableException { + + public DepositAccountTransactionNotAllowedException(final Long accountId, final String action, final DepositAccountType type) { + super("error.msg." + type.resourceName() + ".account.trasaction." + action + ".notallowed", SavingsEnumerations.depositType(type).getValue() + + "account " + action + " transaction not allowed with account identifier " + accountId, accountId); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/FixedDepositProductNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/FixedDepositProductNotFoundException.java new file mode 100644 index 000000000..5b9af4579 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/FixedDepositProductNotFoundException.java @@ -0,0 +1,15 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +public class FixedDepositProductNotFoundException extends AbstractPlatformResourceNotFoundException { + + public FixedDepositProductNotFoundException(final Long id) { + super("error.msg.fixeddepositproduct.id.invalid", "Fixed deposit product with identifier " + id + " does not exist", id); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/RecurringDepositProductNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/RecurringDepositProductNotFoundException.java new file mode 100644 index 000000000..9b4649521 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/RecurringDepositProductNotFoundException.java @@ -0,0 +1,15 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +public class RecurringDepositProductNotFoundException extends AbstractPlatformResourceNotFoundException { + + public RecurringDepositProductNotFoundException(final Long id) { + super("error.msg.recurringdepositproduct.id.invalid", "Recurring deposit product with identifier " + id + " does not exist", id); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/ActivateDepositAccountCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/ActivateDepositAccountCommandHandler.java new file mode 100644 index 000000000..2d6311210 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/ActivateDepositAccountCommandHandler.java @@ -0,0 +1,31 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.DepositAccountWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class ActivateDepositAccountCommandHandler implements NewCommandSourceHandler { + + private final DepositAccountWritePlatformService depositAccountWritePlatformService; + + @Autowired + public ActivateDepositAccountCommandHandler(final DepositAccountWritePlatformService depositAccountWritePlatformService) { + this.depositAccountWritePlatformService = depositAccountWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.depositAccountWritePlatformService.activate(command.entityId(), command, command.depositAccounttype()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/CalculateInterestDepositAccountCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/CalculateInterestDepositAccountCommandHandler.java new file mode 100644 index 000000000..150d8d0fc --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/CalculateInterestDepositAccountCommandHandler.java @@ -0,0 +1,31 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.DepositAccountWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class CalculateInterestDepositAccountCommandHandler implements NewCommandSourceHandler { + + private final DepositAccountWritePlatformService depositAccountWritePlatformService; + + @Autowired + public CalculateInterestDepositAccountCommandHandler(final DepositAccountWritePlatformService depositAccountWritePlatformService) { + this.depositAccountWritePlatformService = depositAccountWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.depositAccountWritePlatformService.calculateInterest(command.entityId(), command.depositAccounttype()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/CloseDepositAccountCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/CloseDepositAccountCommandHandler.java new file mode 100644 index 000000000..d17fce265 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/CloseDepositAccountCommandHandler.java @@ -0,0 +1,31 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.DepositAccountWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class CloseDepositAccountCommandHandler implements NewCommandSourceHandler { + + private final DepositAccountWritePlatformService depositAccountWritePlatformService; + + @Autowired + public CloseDepositAccountCommandHandler(final DepositAccountWritePlatformService depositAccountWritePlatformService) { + this.depositAccountWritePlatformService = depositAccountWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.depositAccountWritePlatformService.close(command.entityId(), command, command.depositAccounttype()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/CreateFixedDepositProductCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/CreateFixedDepositProductCommandHandler.java new file mode 100644 index 000000000..fbec1755f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/CreateFixedDepositProductCommandHandler.java @@ -0,0 +1,29 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.FixedDepositProductWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class CreateFixedDepositProductCommandHandler implements NewCommandSourceHandler { + + private final FixedDepositProductWritePlatformService writePlatformService; + + @Autowired + public CreateFixedDepositProductCommandHandler(final FixedDepositProductWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.create(command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/CreateRecurringDepositProductCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/CreateRecurringDepositProductCommandHandler.java new file mode 100644 index 000000000..45a084b5d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/CreateRecurringDepositProductCommandHandler.java @@ -0,0 +1,29 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.RecurringDepositProductWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class CreateRecurringDepositProductCommandHandler implements NewCommandSourceHandler { + + private final RecurringDepositProductWritePlatformService writePlatformService; + + @Autowired + public CreateRecurringDepositProductCommandHandler(final RecurringDepositProductWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.create(command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DeleteFixedDepositProductCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DeleteFixedDepositProductCommandHandler.java new file mode 100644 index 000000000..ffddafbdb --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DeleteFixedDepositProductCommandHandler.java @@ -0,0 +1,29 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.FixedDepositProductWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class DeleteFixedDepositProductCommandHandler implements NewCommandSourceHandler { + + private final FixedDepositProductWritePlatformService writePlatformService; + + @Autowired + public DeleteFixedDepositProductCommandHandler(final FixedDepositProductWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.delete(command.entityId()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DeleteRecurringDepositProductCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DeleteRecurringDepositProductCommandHandler.java new file mode 100644 index 000000000..1f6838da0 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DeleteRecurringDepositProductCommandHandler.java @@ -0,0 +1,29 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.RecurringDepositProductWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class DeleteRecurringDepositProductCommandHandler implements NewCommandSourceHandler { + + private final RecurringDepositProductWritePlatformService writePlatformService; + + @Autowired + public DeleteRecurringDepositProductCommandHandler(final RecurringDepositProductWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.delete(command.entityId()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationApprovalCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationApprovalCommandHandler.java new file mode 100644 index 000000000..2ed8a133d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationApprovalCommandHandler.java @@ -0,0 +1,32 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.DepositApplicationProcessWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class DepositAccountApplicationApprovalCommandHandler implements NewCommandSourceHandler { + + private final DepositApplicationProcessWritePlatformService depositAccountWritePlatformService; + + @Autowired + public DepositAccountApplicationApprovalCommandHandler( + final DepositApplicationProcessWritePlatformService depositAccountWritePlatformService) { + this.depositAccountWritePlatformService = depositAccountWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.depositAccountWritePlatformService.approveApplication(command.entityId(), command, command.depositAccounttype()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationApprovalUndoCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationApprovalUndoCommandHandler.java new file mode 100644 index 000000000..7766a24ad --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationApprovalUndoCommandHandler.java @@ -0,0 +1,32 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.DepositApplicationProcessWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class DepositAccountApplicationApprovalUndoCommandHandler implements NewCommandSourceHandler { + + private final DepositApplicationProcessWritePlatformService depositAccountWritePlatformService; + + @Autowired + public DepositAccountApplicationApprovalUndoCommandHandler( + final DepositApplicationProcessWritePlatformService depositAccountWritePlatformService) { + this.depositAccountWritePlatformService = depositAccountWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.depositAccountWritePlatformService.undoApplicationApproval(command.entityId(), command, command.depositAccounttype()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationDeletionCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationDeletionCommandHandler.java new file mode 100644 index 000000000..6fb850777 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationDeletionCommandHandler.java @@ -0,0 +1,32 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.DepositApplicationProcessWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class DepositAccountApplicationDeletionCommandHandler implements NewCommandSourceHandler { + + private final DepositApplicationProcessWritePlatformService depositAccountWritePlatformService; + + @Autowired + public DepositAccountApplicationDeletionCommandHandler( + final DepositApplicationProcessWritePlatformService depositAccountWritePlatformService) { + this.depositAccountWritePlatformService = depositAccountWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.depositAccountWritePlatformService.deleteApplication(command.entityId(), command.depositAccounttype()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationModificationCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationModificationCommandHandler.java new file mode 100644 index 000000000..e2fcde8bb --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationModificationCommandHandler.java @@ -0,0 +1,32 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.DepositApplicationProcessWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class DepositAccountApplicationModificationCommandHandler implements NewCommandSourceHandler { + + private final DepositApplicationProcessWritePlatformService depositAccountWritePlatformService; + + @Autowired + public DepositAccountApplicationModificationCommandHandler( + final DepositApplicationProcessWritePlatformService depositAccountWritePlatformService) { + this.depositAccountWritePlatformService = depositAccountWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.depositAccountWritePlatformService.modifyApplication(command.entityId(), command, command.depositAccounttype()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationRejectedCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationRejectedCommandHandler.java new file mode 100644 index 000000000..7776e2da3 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationRejectedCommandHandler.java @@ -0,0 +1,32 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.DepositApplicationProcessWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class DepositAccountApplicationRejectedCommandHandler implements NewCommandSourceHandler { + + private final DepositApplicationProcessWritePlatformService depositAccountWritePlatformService; + + @Autowired + public DepositAccountApplicationRejectedCommandHandler( + final DepositApplicationProcessWritePlatformService depositAccountWritePlatformService) { + this.depositAccountWritePlatformService = depositAccountWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.depositAccountWritePlatformService.rejectApplication(command.entityId(), command, command.depositAccounttype()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationSubmittalCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationSubmittalCommandHandler.java new file mode 100644 index 000000000..4d440bf76 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationSubmittalCommandHandler.java @@ -0,0 +1,32 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.DepositApplicationProcessWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class DepositAccountApplicationSubmittalCommandHandler implements NewCommandSourceHandler { + + private final DepositApplicationProcessWritePlatformService depositAccountWritePlatformService; + + @Autowired + public DepositAccountApplicationSubmittalCommandHandler( + final DepositApplicationProcessWritePlatformService depositAccountWritePlatformService) { + this.depositAccountWritePlatformService = depositAccountWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.depositAccountWritePlatformService.submitApplication(command, command.depositAccounttype()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationWithdrawnByApplicantCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationWithdrawnByApplicantCommandHandler.java new file mode 100644 index 000000000..490af5aee --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountApplicationWithdrawnByApplicantCommandHandler.java @@ -0,0 +1,33 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.DepositApplicationProcessWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class DepositAccountApplicationWithdrawnByApplicantCommandHandler implements NewCommandSourceHandler { + + private final DepositApplicationProcessWritePlatformService depositAccountWritePlatformService; + + @Autowired + public DepositAccountApplicationWithdrawnByApplicantCommandHandler( + final DepositApplicationProcessWritePlatformService depositAccountWritePlatformService) { + this.depositAccountWritePlatformService = depositAccountWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.depositAccountWritePlatformService.applicantWithdrawsFromApplication(command.entityId(), command, + command.depositAccounttype()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountDepositCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountDepositCommandHandler.java new file mode 100644 index 000000000..74da4561c --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositAccountDepositCommandHandler.java @@ -0,0 +1,31 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.DepositAccountWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class DepositAccountDepositCommandHandler implements NewCommandSourceHandler { + + private final DepositAccountWritePlatformService depositAccountWritePlatformService; + + @Autowired + public DepositAccountDepositCommandHandler(final DepositAccountWritePlatformService depositAccountWritePlatformService) { + this.depositAccountWritePlatformService = depositAccountWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.depositAccountWritePlatformService.deposit(command.entityId(), command, command.depositAccounttype()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositTransactionAdjustmentCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositTransactionAdjustmentCommandHandler.java new file mode 100644 index 000000000..5e879301f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/DepositTransactionAdjustmentCommandHandler.java @@ -0,0 +1,33 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.DepositAccountWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class DepositTransactionAdjustmentCommandHandler implements NewCommandSourceHandler { + + private final DepositAccountWritePlatformService depositAccountWritePlatformService; + + @Autowired + public DepositTransactionAdjustmentCommandHandler(final DepositAccountWritePlatformService depositAccountWritePlatformService) { + this.depositAccountWritePlatformService = depositAccountWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + final Long transactionId = Long.valueOf(command.getTransactionId()); + return this.depositAccountWritePlatformService.adjustDepositTransaction(command.entityId(), transactionId, command, + command.depositAccounttype()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/PostInterestDepositAccountCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/PostInterestDepositAccountCommandHandler.java new file mode 100644 index 000000000..ddf850ad2 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/PostInterestDepositAccountCommandHandler.java @@ -0,0 +1,31 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.DepositAccountWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class PostInterestDepositAccountCommandHandler implements NewCommandSourceHandler { + + private final DepositAccountWritePlatformService depositAccountWritePlatformService; + + @Autowired + public PostInterestDepositAccountCommandHandler(final DepositAccountWritePlatformService depositAccountWritePlatformService) { + this.depositAccountWritePlatformService = depositAccountWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.depositAccountWritePlatformService.postInterest(command.entityId(), command.depositAccounttype()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/PrematureCloseDepositAccountCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/PrematureCloseDepositAccountCommandHandler.java new file mode 100644 index 000000000..f2e794faf --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/PrematureCloseDepositAccountCommandHandler.java @@ -0,0 +1,31 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.DepositAccountWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class PrematureCloseDepositAccountCommandHandler implements NewCommandSourceHandler { + + private final DepositAccountWritePlatformService depositAccountWritePlatformService; + + @Autowired + public PrematureCloseDepositAccountCommandHandler(final DepositAccountWritePlatformService depositAccountWritePlatformService) { + this.depositAccountWritePlatformService = depositAccountWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.depositAccountWritePlatformService.prematureClose(command.entityId(), command, command.depositAccounttype()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/UndoTransactionDepositAccountCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/UndoTransactionDepositAccountCommandHandler.java new file mode 100644 index 000000000..10f3b7234 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/UndoTransactionDepositAccountCommandHandler.java @@ -0,0 +1,33 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.DepositAccountWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class UndoTransactionDepositAccountCommandHandler implements NewCommandSourceHandler { + + private final DepositAccountWritePlatformService depositAccountWritePlatformService; + + @Autowired + public UndoTransactionDepositAccountCommandHandler(final DepositAccountWritePlatformService depositAccountWritePlatformService) { + this.depositAccountWritePlatformService = depositAccountWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + final Long transactionId = Long.valueOf(command.getTransactionId()); + return this.depositAccountWritePlatformService.undoTransaction(command.entityId(), transactionId, false, + command.depositAccounttype()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/UpdateFixedDepositProductCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/UpdateFixedDepositProductCommandHandler.java new file mode 100644 index 000000000..df1eb1ccb --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/UpdateFixedDepositProductCommandHandler.java @@ -0,0 +1,29 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.FixedDepositProductWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class UpdateFixedDepositProductCommandHandler implements NewCommandSourceHandler { + + private final FixedDepositProductWritePlatformService writePlatformService; + + @Autowired + public UpdateFixedDepositProductCommandHandler(final FixedDepositProductWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.update(command.entityId(), command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/UpdateRecurringDepositProductCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/UpdateRecurringDepositProductCommandHandler.java new file mode 100644 index 000000000..4c311459a --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/UpdateRecurringDepositProductCommandHandler.java @@ -0,0 +1,29 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.RecurringDepositProductWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class UpdateRecurringDepositProductCommandHandler implements NewCommandSourceHandler { + + private final RecurringDepositProductWritePlatformService writePlatformService; + + @Autowired + public UpdateRecurringDepositProductCommandHandler(final RecurringDepositProductWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.update(command.entityId(), command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/WithdrawDepositAccountCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/WithdrawDepositAccountCommandHandler.java new file mode 100644 index 000000000..658a392a1 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/WithdrawDepositAccountCommandHandler.java @@ -0,0 +1,31 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.DepositAccountWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class WithdrawDepositAccountCommandHandler implements NewCommandSourceHandler { + + private final DepositAccountWritePlatformService depositAccountWritePlatformService; + + @Autowired + public WithdrawDepositAccountCommandHandler(final DepositAccountWritePlatformService depositAccountWritePlatformService) { + this.depositAccountWritePlatformService = depositAccountWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.depositAccountWritePlatformService.withdrawal(command.entityId(), command, command.depositAccounttype()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountInterestRateChartReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountInterestRateChartReadPlatformService.java new file mode 100644 index 000000000..01b3248c3 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountInterestRateChartReadPlatformService.java @@ -0,0 +1,21 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.service; + +import org.mifosplatform.portfolio.savings.data.DepositAccountInterestRateChartData; + +public interface DepositAccountInterestRateChartReadPlatformService { + + DepositAccountInterestRateChartData retrieveOne(Long interestChartId); + + DepositAccountInterestRateChartData retrieveOneWithSlabs(Long interestChartId); + + DepositAccountInterestRateChartData retrieveWithTemplate(DepositAccountInterestRateChartData DepositAccountInterestRateChartData); + + DepositAccountInterestRateChartData retrieveOneWithSlabsOnAccountId(Long accountId); + + DepositAccountInterestRateChartData template(); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountInterestRateChartReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountInterestRateChartReadPlatformServiceImpl.java new file mode 100644 index 000000000..3294a6216 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountInterestRateChartReadPlatformServiceImpl.java @@ -0,0 +1,251 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.service; + +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.infrastructure.core.domain.JdbcSupport; +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.interestratechart.InterestRateChartPeriodType; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartDropdownReadPlatformService; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartEnumerations; +import org.mifosplatform.portfolio.savings.data.DepositAccountInterestRateChartData; +import org.mifosplatform.portfolio.savings.data.DepositAccountInterestRateChartSlabData; +import org.mifosplatform.portfolio.savings.exception.DepositAccountInterestRateChartNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.ResultSetExtractor; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class DepositAccountInterestRateChartReadPlatformServiceImpl implements DepositAccountInterestRateChartReadPlatformService { + + private final PlatformSecurityContext context; + private final JdbcTemplate jdbcTemplate; + private final DepositAccountInterestRateChartMapper chartRowMapper = new DepositAccountInterestRateChartMapper(); + private final DepositAccountInterestRateChartExtractor chartExtractor = new DepositAccountInterestRateChartExtractor(); + private final InterestRateChartDropdownReadPlatformService chartDropdownReadPlatformService; + + @Autowired + public DepositAccountInterestRateChartReadPlatformServiceImpl(PlatformSecurityContext context, final RoutingDataSource dataSource, + InterestRateChartDropdownReadPlatformService chartDropdownReadPlatformService) { + this.context = context; + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.chartDropdownReadPlatformService = chartDropdownReadPlatformService; + } + + @Override + public DepositAccountInterestRateChartData retrieveOne(Long chartId) { + try { + this.context.authenticatedUser(); + final String sql = "select " + this.chartRowMapper.schema() + " where irc.id = ? "; + return this.jdbcTemplate.queryForObject(sql, this.chartRowMapper, new Object[] { chartId }); + } catch (final EmptyResultDataAccessException e) { + throw new DepositAccountInterestRateChartNotFoundException(chartId); + } + } + + @Override + public DepositAccountInterestRateChartData retrieveOneWithSlabs(Long chartId) { + this.context.authenticatedUser(); + final String sql = "select " + this.chartExtractor.schema() + " where irc.id = ? order by ircd.id asc"; + Collection chartDatas = this.jdbcTemplate.query(sql, this.chartExtractor, + new Object[] { chartId }); + if (chartDatas == null || chartDatas.isEmpty()) { throw new DepositAccountInterestRateChartNotFoundException(chartId); } + + return chartDatas.iterator().next(); + } + + @Override + public DepositAccountInterestRateChartData retrieveWithTemplate(DepositAccountInterestRateChartData chartData) { + return DepositAccountInterestRateChartData.withTemplate(chartData, + this.chartDropdownReadPlatformService.retrievePeriodTypeOptions()); + } + + @Override + public DepositAccountInterestRateChartData retrieveOneWithSlabsOnAccountId(Long accountId) { + this.context.authenticatedUser(); + final String sql = "select " + this.chartExtractor.schema() + " where irc.savings_account_id = ? order by ircd.id asc"; + Collection chartDatas = this.jdbcTemplate.query(sql, this.chartExtractor, + new Object[] { accountId }); + if (chartDatas == null || chartDatas.isEmpty()) { throw new DepositAccountInterestRateChartNotFoundException(accountId); } + + return chartDatas.iterator().next(); + } + + @Override + public DepositAccountInterestRateChartData template() { + return DepositAccountInterestRateChartData.template(this.chartDropdownReadPlatformService.retrievePeriodTypeOptions()); + } + + private static final class DepositAccountInterestRateChartExtractor implements + ResultSetExtractor> { + + DepositAccountInterestRateChartMapper chartMapper = new DepositAccountInterestRateChartMapper(); + DepositAccountInterestRateChartSlabsMapper chartSlabsMapper = new DepositAccountInterestRateChartSlabsMapper(); + + private final String schemaSql; + + public String schema() { + return this.schemaSql; + } + + private DepositAccountInterestRateChartExtractor() { + final StringBuilder sqlBuilder = new StringBuilder(400); + + sqlBuilder + .append("irc.id as ircId, irc.name as ircName, irc.description as ircDescription,") + .append("irc.from_date as ircFromDate, irc.end_date as ircEndDate, ") + .append("ircd.id as ircdId, ircd.description as ircdDescription, ircd.period_type_enum ircdPeriodTypeId, ") + .append("ircd.from_period as ircdFromPeriod, ircd.to_period as ircdToPeriod, ircd.amount_range_from as ircdAmountRangeFrom, ") + .append("ircd.amount_range_to as ircdAmountRangeTo, ircd.annual_interest_rate as ircdAnnualInterestRate, ") + .append("curr.code as currencyCode, curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ") + .append("curr.display_symbol as currencyDisplaySymbol, curr.decimal_places as currencyDigits, curr.currency_multiplesof as inMultiplesOf, ") + .append("sa.id as accountId, sa.account_no as accountNumber ") + .append("from ") + .append("m_savings_account_interest_rate_chart irc left join m_savings_account_interest_rate_slab ircd on irc.id=ircd.savings_account_interest_rate_chart_id ") + .append("left join m_currency curr on ircd.currency_code= curr.code ") + .append("left join m_savings_account sa on irc.savings_account_id=sa.id "); + + this.schemaSql = sqlBuilder.toString(); + } + + @Override + public Collection extractData(ResultSet rs) throws SQLException, DataAccessException { + + List chartDataList = new ArrayList(); + + DepositAccountInterestRateChartData chartData = null; + Long interestRateChartId = null; + int ircIndex = 0;// Interest rate chart index + int ircdIndex = 0;// Interest rate chart Slabs index + + while (rs.next()) { + Long tempIrcId = rs.getLong("ircId"); + // first row or when interest rate chart id changes + if (chartData == null || (interestRateChartId != null && !interestRateChartId.equals(tempIrcId))) { + + interestRateChartId = tempIrcId; + chartData = chartMapper.mapRow(rs, ircIndex++); + chartDataList.add(chartData); + ircdIndex = 0;// reset index + + } + final DepositAccountInterestRateChartSlabData chartSlabsData = chartSlabsMapper.mapRow(rs, ircdIndex++); + if (chartSlabsData != null) { + chartData.addChartSlab(chartSlabsData); + } + } + return chartDataList; + } + + } + + public static final class DepositAccountInterestRateChartMapper implements RowMapper { + + private final String schemaSql; + + public String schema() { + return this.schemaSql; + } + + private DepositAccountInterestRateChartMapper() { + final StringBuilder sqlBuilder = new StringBuilder(400); + + sqlBuilder.append("irc.id as ircId, irc.name as ircName, irc.description as ircDescription, ") + .append("irc.from_date as ircFromDate, irc.end_date as ircEndDate, ") + .append("sa.id as accountId, sa.account_no as accountNumber ").append("from ") + .append("m_savings_account_interest_rate_chart irc left join m_savings_account sa on irc.savings_account_id=sa.id "); + this.schemaSql = sqlBuilder.toString(); + } + + @Override + public DepositAccountInterestRateChartData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException { + final Long id = rs.getLong("ircId"); + final String name = rs.getString("ircName"); + final String description = rs.getString("ircDescription"); + final LocalDate fromDate = JdbcSupport.getLocalDate(rs, "ircFromDate"); + final LocalDate endDate = JdbcSupport.getLocalDate(rs, "ircEndDate"); + final Long accountId = rs.getLong("accountId"); + final String accountNumber = rs.getString("accountNumber"); + final Collection periodTypes = InterestRateChartEnumerations.periodType(InterestRateChartPeriodType.values()); + return DepositAccountInterestRateChartData.instance(id, name, description, fromDate, endDate, accountId, accountNumber, + null, periodTypes); + } + + } + + private static final class DepositAccountInterestRateChartSlabsMapper implements RowMapper { + + /* + * private final String schemaSql; + * + * public String schema() { return this.schemaSql; } + * + * private DepositAccountInterestRateChartSlabsMapper() { final + * StringBuilder sqlBuilder = new StringBuilder(400); + * + * sqlBuilder .append( + * "ircd.id as ircdId, ircd.description as ircdDescription, ircd.period_type_enum ircdPeriodTypeId, " + * ) .append( + * "ircd.from_period as ircdFromPeriod, ircd.to_period as ircdToPeriod, ircd.amount_range_from as ircdAmountRangeFrom, " + * ) .append( + * "ircd.amount_range_to as ircdAmountRangeTo, ircd.annual_interest_rate as ircdAnnualInterestRate, " + * ) .append( + * "curr.code as currencyCode, curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, " + * ) .append( + * "curr.display_symbol as currencyDisplaySymbol, curr.decimal_places as currencyDigits, curr.currency_multiplesof as inMultiplesOf " + * ) .append("from ").append( + * "m_savings_account_interest_rate_slab ircd ") + * .append("left join m_currency curr on ircd.currency_code= curr.code " + * ); this.schemaSql = sqlBuilder.toString(); } + */ + + @Override + public DepositAccountInterestRateChartSlabData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException { + final Long id = JdbcSupport.getLongDefaultToNullIfZero(rs, "ircdId"); + // If there are not chart Slabs are associated then in + // InterestRateChartExtractor the chart Slabs id will be null. + if (id == null) { return null; } + + final String description = rs.getString("ircdDescription"); + final Integer fromPeriod = JdbcSupport.getInteger(rs, "ircdFromPeriod"); + final Integer toPeriod = JdbcSupport.getInteger(rs, "ircdToPeriod"); + final Integer periodTypeId = JdbcSupport.getInteger(rs, "ircdPeriodTypeId"); + final EnumOptionData periodType = InterestRateChartEnumerations.periodType(periodTypeId); + final BigDecimal amountRangeFrom = rs.getBigDecimal("ircdAmountRangeFrom"); + final BigDecimal amountRangeTo = rs.getBigDecimal("ircdAmountRangeTo"); + final BigDecimal annualInterestRate = rs.getBigDecimal("ircdAnnualInterestRate"); + + // currency Slabs + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + // currency + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, + currencyDisplaySymbol, currencyNameCode); + + return DepositAccountInterestRateChartSlabData.instance(id, description, periodType, fromPeriod, toPeriod, amountRangeFrom, + amountRangeTo, annualInterestRate, currency); + } + + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountPreMatureCalculationPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountPreMatureCalculationPlatformService.java new file mode 100644 index 000000000..17f5d3b90 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountPreMatureCalculationPlatformService.java @@ -0,0 +1,15 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.service; + +import org.mifosplatform.infrastructure.core.api.JsonQuery; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.data.DepositAccountData; + +public interface DepositAccountPreMatureCalculationPlatformService { + + DepositAccountData calculatePreMatureAmount(Long accountId, JsonQuery query, DepositAccountType depositAccountType); +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountPreMatureCalculationPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountPreMatureCalculationPlatformServiceImpl.java new file mode 100644 index 000000000..635e35e14 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountPreMatureCalculationPlatformServiceImpl.java @@ -0,0 +1,87 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.service; + +import static org.mifosplatform.portfolio.savings.DepositsApiConstants.closedOnDateParamName; + +import java.util.Collection; + +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.codes.data.CodeValueData; +import org.mifosplatform.infrastructure.codes.service.CodeValueReadPlatformService; +import org.mifosplatform.infrastructure.core.api.JsonQuery; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants; +import org.mifosplatform.portfolio.savings.DepositAccountOnClosureType; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.data.DepositAccountData; +import org.mifosplatform.portfolio.savings.data.DepositAccountTransactionDataValidator; +import org.mifosplatform.portfolio.savings.data.FixedDepositAccountData; +import org.mifosplatform.portfolio.savings.data.RecurringDepositAccountData; +import org.mifosplatform.portfolio.savings.data.SavingsAccountData; +import org.mifosplatform.portfolio.savings.domain.DepositAccountAssembler; +import org.mifosplatform.portfolio.savings.domain.FixedDepositAccount; +import org.mifosplatform.portfolio.savings.domain.RecurringDepositAccount; +import org.mifosplatform.portfolio.savings.domain.SavingsAccount; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.google.gson.JsonElement; + +@Service +public class DepositAccountPreMatureCalculationPlatformServiceImpl implements DepositAccountPreMatureCalculationPlatformService { + + private final FromJsonHelper fromJsonHelper; + private final DepositAccountTransactionDataValidator depositAccountTransactionDataValidator; + private final DepositAccountAssembler depositAccountAssembler; + private final CodeValueReadPlatformService codeValueReadPlatformService; + private final SavingsAccountReadPlatformService savingsAccountReadPlatformService; + + @Autowired + public DepositAccountPreMatureCalculationPlatformServiceImpl(final FromJsonHelper fromJsonHelper, + final DepositAccountTransactionDataValidator depositAccountTransactionDataValidator, + final DepositAccountAssembler depositAccountAssembler, final CodeValueReadPlatformService codeValueReadPlatformService, + final SavingsAccountReadPlatformService savingsAccountReadPlatformService) { + this.fromJsonHelper = fromJsonHelper; + this.depositAccountTransactionDataValidator = depositAccountTransactionDataValidator; + this.depositAccountAssembler = depositAccountAssembler; + this.codeValueReadPlatformService = codeValueReadPlatformService; + this.savingsAccountReadPlatformService = savingsAccountReadPlatformService; + + } + + @Transactional + @Override + public DepositAccountData calculatePreMatureAmount(final Long accountId, final JsonQuery query, + final DepositAccountType depositAccountType) { + this.depositAccountTransactionDataValidator.validatePreMatureAmountCalculation(query.json(), depositAccountType); + final SavingsAccount account = this.depositAccountAssembler.assembleFrom(accountId, depositAccountType); + + DepositAccountData accountData = null; + Collection onAccountClosureOptions = SavingsEnumerations.depositAccountOnClosureType(DepositAccountOnClosureType + .values()); + final Collection paymentTypeOptions = this.codeValueReadPlatformService + .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); + final Collection savingsAccountDatas = this.savingsAccountReadPlatformService.retrieveActiveForLookup( + account.clientId(), DepositAccountType.SAVINGS_DEPOSIT); + final JsonElement element = this.fromJsonHelper.parse(query.json()); + final LocalDate preMaturityDate = this.fromJsonHelper.extractLocalDateNamed(closedOnDateParamName, element); + + if (depositAccountType.isFixedDeposit()) { + final FixedDepositAccount fd = (FixedDepositAccount) account; + accountData = FixedDepositAccountData.preClosureDetails(account.getId(), fd.calculatePreMatureAmount(preMaturityDate), + onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas); + } else if (depositAccountType.isRecurringDeposit()) { + final RecurringDepositAccount rd = (RecurringDepositAccount) account; + accountData = RecurringDepositAccountData.preClosureDetails(account.getId(), rd.calculatePreMatureAmount(preMaturityDate), + onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas); + } + + return accountData; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformService.java new file mode 100644 index 000000000..27a0c409a --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformService.java @@ -0,0 +1,37 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.service; + +import java.util.Collection; + +import org.mifosplatform.infrastructure.core.data.PaginationParameters; +import org.mifosplatform.infrastructure.core.service.Page; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.data.DepositAccountData; +import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionData; + +public interface DepositAccountReadPlatformService { + + Collection retrieveAll(final DepositAccountType depositAccountType, final PaginationParameters paginationParameters); + + Page retrieveAllPaged(final DepositAccountType depositAccountType, final PaginationParameters paginationParameters); + + Collection retrieveAllForLookup(final DepositAccountType depositAccountType); + + DepositAccountData retrieveOne(final DepositAccountType depositAccountType, Long accountId); + + DepositAccountData retrieveOneWithClosureTemplate(final DepositAccountType depositAccountType, Long accountId); + + DepositAccountData retrieveOneWithChartSlabs(final DepositAccountType depositAccountType, Long productId); + + Collection retrieveAllTransactions(final DepositAccountType depositAccountType, Long accountId); + + DepositAccountData retrieveTemplate(final DepositAccountType depositAccountType, Long clientId, Long groupId, Long productId, boolean staffInSelectedOfficeOnly); + + Collection retrieveForMaturityUpdate(); + + SavingsAccountTransactionData retrieveRecurringAccountDepositTransactionTemplate(final Long accountId); +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java new file mode 100644 index 000000000..c773d7d4e --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java @@ -0,0 +1,1372 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.service; + +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; + +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.codes.data.CodeValueData; +import org.mifosplatform.infrastructure.codes.service.CodeValueReadPlatformService; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.infrastructure.core.data.PaginationParameters; +import org.mifosplatform.infrastructure.core.data.PaginationParametersDataValidator; +import org.mifosplatform.infrastructure.core.domain.JdbcSupport; +import org.mifosplatform.infrastructure.core.service.DateUtils; +import org.mifosplatform.infrastructure.core.service.Page; +import org.mifosplatform.infrastructure.core.service.PaginationHelper; +import org.mifosplatform.infrastructure.core.service.RoutingDataSource; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.organisation.monetary.data.CurrencyData; +import org.mifosplatform.organisation.staff.data.StaffData; +import org.mifosplatform.organisation.staff.service.StaffReadPlatformService; +import org.mifosplatform.portfolio.account.data.AccountTransferData; +import org.mifosplatform.portfolio.charge.data.ChargeData; +import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService; +import org.mifosplatform.portfolio.client.data.ClientData; +import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; +import org.mifosplatform.portfolio.group.data.GroupGeneralData; +import org.mifosplatform.portfolio.group.service.GroupReadPlatformService; +import org.mifosplatform.portfolio.interestratechart.data.InterestRateChartData; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartEnumerations; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartReadPlatformService; +import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants; +import org.mifosplatform.portfolio.paymentdetail.data.PaymentDetailData; +import org.mifosplatform.portfolio.savings.DepositAccountOnClosureType; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.DepositsApiConstants; +import org.mifosplatform.portfolio.savings.SavingsAccountTransactionType; +import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType; +import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; +import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; +import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType; +import org.mifosplatform.portfolio.savings.data.DepositAccountData; +import org.mifosplatform.portfolio.savings.data.DepositAccountInterestRateChartData; +import org.mifosplatform.portfolio.savings.data.DepositProductData; +import org.mifosplatform.portfolio.savings.data.FixedDepositAccountData; +import org.mifosplatform.portfolio.savings.data.RecurringDepositAccountData; +import org.mifosplatform.portfolio.savings.data.SavingsAccountApplicationTimelineData; +import org.mifosplatform.portfolio.savings.data.SavingsAccountChargeData; +import org.mifosplatform.portfolio.savings.data.SavingsAccountData; +import org.mifosplatform.portfolio.savings.data.SavingsAccountStatusEnumData; +import org.mifosplatform.portfolio.savings.data.SavingsAccountSummaryData; +import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionData; +import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionEnumData; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountStatusType; +import org.mifosplatform.portfolio.savings.exception.DepositAccountNotFoundException; +import org.mifosplatform.useradministration.domain.AppUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +@Service +public class DepositAccountReadPlatformServiceImpl implements DepositAccountReadPlatformService { + + private final PlatformSecurityContext context; + private final JdbcTemplate jdbcTemplate; + private final DepositAccountInterestRateChartReadPlatformService accountChartReadPlatformService; + private final InterestRateChartReadPlatformService productChartReadPlatformService; + private final FixedDepositAccountMapper fixedDepositAccountRowMapper = new FixedDepositAccountMapper(); + private final RecurringDepositAccountMapper recurringDepositAccountRowMapper = new RecurringDepositAccountMapper(); + private final DepositAccountLookupMapper depositAccountLookupsRowMapper = new DepositAccountLookupMapper(); + private final DepositAccountForMaturityMapper depositAccountForMaturityRowMapper = new DepositAccountForMaturityMapper(); + private final PaginationParametersDataValidator paginationParametersDataValidator; + private final PaginationHelper paginationHelper = new PaginationHelper(); + private final SavingsAccountTransactionsMapper transactionsMapper; + private final ClientReadPlatformService clientReadPlatformService; + private final GroupReadPlatformService groupReadPlatformService; + private final DepositProductReadPlatformService depositProductReadPlatformService; + private final SavingsDropdownReadPlatformService savingsDropdownReadPlatformService; + private final ChargeReadPlatformService chargeReadPlatformService; + private final StaffReadPlatformService staffReadPlatformService; + private final DepositsDropdownReadPlatformService depositsDropdownReadPlatformService; + private final CodeValueReadPlatformService codeValueReadPlatformService; + private final SavingsAccountReadPlatformService savingsAccountReadPlatformService; + private final RecurringAccountDepositTransactionTemplateMapper rdTransactionTemplateMapper; + + @Autowired + public DepositAccountReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource, + final DepositAccountInterestRateChartReadPlatformService chartReadPlatformService, + final PaginationParametersDataValidator paginationParametersDataValidator, + final ClientReadPlatformService clientReadPlatformService, final GroupReadPlatformService groupReadPlatformService, + final DepositProductReadPlatformService depositProductReadPlatformService, + final SavingsDropdownReadPlatformService savingsDropdownReadPlatformService, + final ChargeReadPlatformService chargeReadPlatformService, final StaffReadPlatformService staffReadPlatformService, + final DepositsDropdownReadPlatformService depositsDropdownReadPlatformService, + final InterestRateChartReadPlatformService productChartReadPlatformService, + final CodeValueReadPlatformService codeValueReadPlatformService, + final SavingsAccountReadPlatformService savingsAccountReadPlatformService) { + this.context = context; + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.accountChartReadPlatformService = chartReadPlatformService; + this.paginationParametersDataValidator = paginationParametersDataValidator; + this.transactionsMapper = new SavingsAccountTransactionsMapper(); + this.clientReadPlatformService = clientReadPlatformService; + this.groupReadPlatformService = groupReadPlatformService; + this.depositProductReadPlatformService = depositProductReadPlatformService; + this.savingsDropdownReadPlatformService = savingsDropdownReadPlatformService; + this.chargeReadPlatformService = chargeReadPlatformService; + this.staffReadPlatformService = staffReadPlatformService; + this.depositsDropdownReadPlatformService = depositsDropdownReadPlatformService; + this.productChartReadPlatformService = productChartReadPlatformService; + this.codeValueReadPlatformService = codeValueReadPlatformService; + this.savingsAccountReadPlatformService = savingsAccountReadPlatformService; + this.rdTransactionTemplateMapper = new RecurringAccountDepositTransactionTemplateMapper(); + } + + @Override + public Collection retrieveAll(final DepositAccountType depositAccountType, + final PaginationParameters paginationParameters) { + + this.context.authenticatedUser(); + final DepositAccountMapper depositAccountMapper = this.getDepositAccountMapper(depositAccountType); + if (depositAccountMapper == null) return null; + + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("select "); + sqlBuilder.append(depositAccountMapper.schema()); + sqlBuilder.append(" where sa.deposit_type_enum = ? "); + // always append at the end of a sql statement + sqlBuilder.append(paginationParameters.paginationSql()); + + return this.jdbcTemplate.query(sqlBuilder.toString(), depositAccountMapper, new Object[] { depositAccountType.getValue() }); + } + + @Override + public Page retrieveAllPaged(final DepositAccountType depositAccountType, + final PaginationParameters paginationParameters) { + + this.paginationParametersDataValidator.validateParameterValues(paginationParameters, DepositsApiConstants.supportedOrderByValues, + depositAccountType.resourceName()); + + final DepositAccountMapper depositAccountMapper = this.getDepositAccountMapper(depositAccountType); + if (depositAccountMapper == null) return null; + + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append(depositAccountMapper.schema()); + sqlBuilder.append(" where sa.deposit_type_enum = ? "); + sqlBuilder.append(paginationParameters.paginationSql()); + + final String sqlCountRows = "SELECT FOUND_ROWS()"; + + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), + new Object[] { depositAccountType.getValue() }, depositAccountMapper); + } + + @Override + public Collection retrieveAllForLookup(final DepositAccountType depositAccountType) { + + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("select "); + sqlBuilder.append(this.depositAccountLookupsRowMapper.schema()); + sqlBuilder.append(" where sa.deposit_type_enum = ? "); + + return this.jdbcTemplate.query(sqlBuilder.toString(), this.depositAccountLookupsRowMapper, + new Object[] { depositAccountType.getValue() }); + } + + @Override + public Collection retrieveForMaturityUpdate() { + + final StringBuilder sqlBuilder = new StringBuilder(200); + sqlBuilder.append("SELECT "); + sqlBuilder.append(this.depositAccountForMaturityRowMapper.schema()); + sqlBuilder.append(" WHERE da.deposit_type_enum in (?, ?) and da.status_enum = ?"); + + return this.jdbcTemplate.query(sqlBuilder.toString(), this.depositAccountForMaturityRowMapper, new Object[] { + DepositAccountType.FIXED_DEPOSIT.getValue(), DepositAccountType.RECURRING_DEPOSIT.getValue(), + SavingsAccountStatusType.ACTIVE.getValue() }); + } + + @Override + public DepositAccountData retrieveOne(final DepositAccountType depositAccountType, final Long accountId) { + try { + this.context.authenticatedUser(); + + final DepositAccountMapper depositAccountMapper = this.getDepositAccountMapper(depositAccountType); + if (depositAccountMapper == null) return null; + + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("select "); + sqlBuilder.append(depositAccountMapper.schema()); + sqlBuilder.append(" where sa.id = ? and sa.deposit_type_enum = ? "); + + return this.jdbcTemplate.queryForObject(sqlBuilder.toString(), depositAccountMapper, new Object[] { accountId, + depositAccountType.getValue() }); + + } catch (final EmptyResultDataAccessException e) { + throw new DepositAccountNotFoundException(depositAccountType, accountId); + } + } + + @Override + public DepositAccountData retrieveOneWithClosureTemplate(final DepositAccountType depositAccountType, final Long accountId) { + try { + this.context.authenticatedUser(); + + final DepositAccountMapper depositAccountMapper = this.getDepositAccountMapper(depositAccountType); + if (depositAccountMapper == null) return null; + + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("select "); + sqlBuilder.append(depositAccountMapper.schema()); + sqlBuilder.append(" where sa.id = ? and sa.deposit_type_enum = ? "); + + DepositAccountData account = this.jdbcTemplate.queryForObject(sqlBuilder.toString(), depositAccountMapper, new Object[] { + accountId, depositAccountType.getValue() }); + Collection onAccountClosureOptions = SavingsEnumerations + .depositAccountOnClosureType(DepositAccountOnClosureType.values()); + final Collection paymentTypeOptions = this.codeValueReadPlatformService + .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); + final Collection savingsAccountDatas = this.savingsAccountReadPlatformService.retrieveActiveForLookup( + account.clientId(), DepositAccountType.SAVINGS_DEPOSIT); + if (depositAccountType.isFixedDeposit()) { + account = FixedDepositAccountData.withClosureTemplateDetails((FixedDepositAccountData) account, onAccountClosureOptions, + paymentTypeOptions, savingsAccountDatas); + } else if (depositAccountType.isRecurringDeposit()) { + account = RecurringDepositAccountData.withClosureTemplateDetails((RecurringDepositAccountData) account, + onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas); + } + + return account; + + } catch (final EmptyResultDataAccessException e) { + throw new DepositAccountNotFoundException(depositAccountType, accountId); + } + } + + @Override + public DepositAccountData retrieveOneWithChartSlabs(final DepositAccountType depositAccountType, Long accountId) { + DepositAccountData depositAccount = this.retrieveOne(depositAccountType, accountId); + DepositAccountInterestRateChartData chart = this.accountChartReadPlatformService.retrieveOneWithSlabsOnAccountId(accountId); + + if (depositAccountType.isFixedDeposit()) { + depositAccount = FixedDepositAccountData.withInterestChart((FixedDepositAccountData) depositAccount, chart); + } else if (depositAccountType.isRecurringDeposit()) { + depositAccount = RecurringDepositAccountData.withInterestChart((RecurringDepositAccountData) depositAccount, chart); + } + + return depositAccount; + } + + @Override + public Collection retrieveAllTransactions(final DepositAccountType depositAccountType, + final Long accountId) { + + final String sql = "select " + this.transactionsMapper.schema() + + " where sa.id = ? and sa.deposit_type_enum = ? order by tr.transaction_date DESC, tr.id DESC"; + + return this.jdbcTemplate.query(sql, this.transactionsMapper, new Object[] { accountId, depositAccountType.getValue() }); + } + + @Override + public DepositAccountData retrieveTemplate(final DepositAccountType depositAccountType, final Long clientId, final Long groupId, + final Long productId, final boolean staffInSelectedOfficeOnly) { + + final AppUser loggedInUser = this.context.authenticatedUser(); + Long officeId = loggedInUser.getOffice().getId(); + + ClientData client = null; + if (clientId != null) { + client = this.clientReadPlatformService.retrieveOne(clientId); + officeId = client.officeId(); + } + + GroupGeneralData group = null; + if (groupId != null) { + group = this.groupReadPlatformService.retrieveOne(groupId); + officeId = group.officeId(); + } + + final Collection preClosurePenalInterestOnTypeOptions = this.depositsDropdownReadPlatformService + .retrievePreClosurePenalInterestOnTypeOptions(); + + final Collection interestFreePeriodTypeOptions = this.depositsDropdownReadPlatformService + .retrieveInterestFreePeriodFrequencyTypeOptions(); + + final Collection depositTermTypeOptions = this.depositsDropdownReadPlatformService.retrieveDepositTermTypeOptions(); + + final Collection depositPeriodFrequencyOptions = this.depositsDropdownReadPlatformService + .retrieveDepositTermTypeOptions(); + + final Collection inMultiplesOfDepositTermTypeOptions = this.depositsDropdownReadPlatformService + .retrieveInMultiplesOfDepositTermTypeOptions(); + final Collection recurringDepositTypeOptions = this.depositsDropdownReadPlatformService + .retrieveRecurringDepositTypeOptions(); + final Collection recurringDepositFrequencyTypeOptions = this.depositsDropdownReadPlatformService + .retrieveRecurringDepositFrequencyTypeOptions(); + + final Collection productOptions = this.depositProductReadPlatformService + .retrieveAllForLookup(depositAccountType); + DepositAccountData template = null; + if (productId != null) { + + final DepositAccountTemplateMapper mapper = getDepositAccountTemplaMapper(depositAccountType, client, group); + + final String sql = "select " + mapper.schema() + " where sa.id = ? and sa.deposit_type_enum = ? "; + template = this.jdbcTemplate.queryForObject(sql, mapper, new Object[] { productId, depositAccountType.getValue() }); + + final Collection interestCompoundingPeriodTypeOptions = this.savingsDropdownReadPlatformService + .retrieveCompoundingInterestPeriodTypeOptions(); + + final Collection interestPostingPeriodTypeOptions = this.savingsDropdownReadPlatformService + .retrieveInterestPostingPeriodTypeOptions(); + + final Collection interestCalculationTypeOptions = this.savingsDropdownReadPlatformService + .retrieveInterestCalculationTypeOptions(); + + final Collection interestCalculationDaysInYearTypeOptions = this.savingsDropdownReadPlatformService + .retrieveInterestCalculationDaysInYearTypeOptions(); + + final Collection lockinPeriodFrequencyTypeOptions = this.savingsDropdownReadPlatformService + .retrieveLockinPeriodFrequencyTypeOptions(); + + final Collection withdrawalFeeTypeOptions = this.savingsDropdownReadPlatformService + .retrievewithdrawalFeeTypeOptions(); + + final Collection transactions = null; + final Collection productCharges = this.chargeReadPlatformService.retrieveSavingsProductCharges(productId); + // update charges from Product charges + final Collection charges = fromChargesToSavingsCharges(productCharges); + + final boolean feeChargesOnly = false; + final Collection chargeOptions = this.chargeReadPlatformService + .retrieveSavingsAccountApplicableCharges(feeChargesOnly); + + Collection fieldOfficerOptions = null; + + if (officeId != null) { + + if (staffInSelectedOfficeOnly) { + // only bring back loan officers in selected branch/office + final Collection fieldOfficersInBranch = this.staffReadPlatformService + .retrieveAllLoanOfficersInOfficeById(officeId); + + if (!CollectionUtils.isEmpty(fieldOfficersInBranch)) { + fieldOfficerOptions = new ArrayList(fieldOfficersInBranch); + } + } else { + // by default bring back all officers in selected + // branch/office as well as officers in office above + // this office + final boolean restrictToLoanOfficersOnly = true; + final Collection loanOfficersInHierarchy = this.staffReadPlatformService + .retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(officeId, restrictToLoanOfficersOnly); + + if (!CollectionUtils.isEmpty(loanOfficersInHierarchy)) { + fieldOfficerOptions = new ArrayList(loanOfficersInHierarchy); + } + } + } + + // retrieve chart Slabs + final InterestRateChartData productChartData = this.productChartReadPlatformService.retrieveActiveChartWithTemplate(productId); + final DepositAccountInterestRateChartData accountChart = DepositAccountInterestRateChartData.from(productChartData); + + if (depositAccountType.isFixedDeposit()) { + + template = FixedDepositAccountData.withTemplateOptions((FixedDepositAccountData) template, productOptions, + fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, + interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, + withdrawalFeeTypeOptions, transactions, charges, chargeOptions, interestFreePeriodTypeOptions, + preClosurePenalInterestOnTypeOptions, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions, + depositPeriodFrequencyOptions); + template = FixedDepositAccountData.withInterestChart((FixedDepositAccountData) template, accountChart); + } else if (depositAccountType.isRecurringDeposit()) { + + template = RecurringDepositAccountData.withTemplateOptions((RecurringDepositAccountData) template, productOptions, + fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, + interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, + withdrawalFeeTypeOptions, transactions, charges, chargeOptions, interestFreePeriodTypeOptions, + preClosurePenalInterestOnTypeOptions, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions, + recurringDepositTypeOptions, recurringDepositFrequencyTypeOptions, depositPeriodFrequencyOptions); + template = RecurringDepositAccountData.withInterestChart((RecurringDepositAccountData) template, accountChart); + + } + + } else { + + String clientName = null; + if (client != null) { + clientName = client.displayName(); + } + + String groupName = null; + if (group != null) { + groupName = group.getName(); + } + + final Collection fieldOfficerOptions = null; + final Collection interestCompoundingPeriodTypeOptions = null; + final Collection interestPostingPeriodTypeOptions = null; + final Collection interestCalculationTypeOptions = null; + final Collection interestCalculationDaysInYearTypeOptions = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + + final Collection transactions = null; + final Collection charges = null; + + final boolean feeChargesOnly = true; + final Collection chargeOptions = this.chargeReadPlatformService + .retrieveSavingsAccountApplicableCharges(feeChargesOnly); + + if (depositAccountType.isFixedDeposit()) { + + template = FixedDepositAccountData.withClientTemplate(clientId, clientName, groupId, groupName); + + template = FixedDepositAccountData.withTemplateOptions((FixedDepositAccountData) template, productOptions, + fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, + interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, + withdrawalFeeTypeOptions, transactions, charges, chargeOptions, interestFreePeriodTypeOptions, + preClosurePenalInterestOnTypeOptions, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions, + depositPeriodFrequencyOptions); + } else if (depositAccountType.isRecurringDeposit()) { + + template = RecurringDepositAccountData.withClientTemplate(clientId, clientName, groupId, groupName); + + template = RecurringDepositAccountData.withTemplateOptions((RecurringDepositAccountData) template, productOptions, + fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, + interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, + withdrawalFeeTypeOptions, transactions, charges, chargeOptions, interestFreePeriodTypeOptions, + preClosurePenalInterestOnTypeOptions, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions, + recurringDepositTypeOptions, recurringDepositFrequencyTypeOptions, depositPeriodFrequencyOptions); + } + } + + return template; + } + + @Override + public SavingsAccountTransactionData retrieveRecurringAccountDepositTransactionTemplate(final Long accountId) { + + try { + final String sql = "select " + this.rdTransactionTemplateMapper.schema() + " where sa.id = ? and sa.deposit_type_enum = ?"; + + return this.jdbcTemplate.queryForObject(sql, this.rdTransactionTemplateMapper, new Object[] { accountId, + DepositAccountType.RECURRING_DEPOSIT.getValue() }); + } catch (final EmptyResultDataAccessException e) { + throw new DepositAccountNotFoundException(DepositAccountType.RECURRING_DEPOSIT, accountId); + } + } + + private static abstract class DepositAccountMapper implements RowMapper { + + private final String selectFieldsSql; + private final String selectTablesSql; + + @Override + public abstract DepositAccountData mapRow(ResultSet rs, int rowNum) throws SQLException; + + public DepositAccountMapper() { + final StringBuilder selectFieldsSqlBuilder = new StringBuilder(400); + selectFieldsSqlBuilder.append("sa.id as id, sa.account_no as accountNo, sa.external_id as externalId, "); + selectFieldsSqlBuilder.append("c.id as clientId, c.display_name as clientName, "); + selectFieldsSqlBuilder.append("g.id as groupId, g.display_name as groupName, "); + selectFieldsSqlBuilder.append("sp.id as productId, sp.name as productName, "); + selectFieldsSqlBuilder.append("s.id fieldOfficerId, s.display_name as fieldOfficerName, "); + selectFieldsSqlBuilder.append("sa.status_enum as statusEnum, "); + selectFieldsSqlBuilder.append("sa.submittedon_date as submittedOnDate,"); + selectFieldsSqlBuilder.append("sbu.username as submittedByUsername,"); + selectFieldsSqlBuilder.append("sbu.firstname as submittedByFirstname, sbu.lastname as submittedByLastname,"); + selectFieldsSqlBuilder.append("sa.rejectedon_date as rejectedOnDate,"); + selectFieldsSqlBuilder.append("rbu.username as rejectedByUsername,"); + selectFieldsSqlBuilder.append("rbu.firstname as rejectedByFirstname, rbu.lastname as rejectedByLastname,"); + selectFieldsSqlBuilder.append("sa.withdrawnon_date as withdrawnOnDate,"); + selectFieldsSqlBuilder.append("wbu.username as withdrawnByUsername,"); + selectFieldsSqlBuilder.append("wbu.firstname as withdrawnByFirstname, wbu.lastname as withdrawnByLastname,"); + selectFieldsSqlBuilder.append("sa.approvedon_date as approvedOnDate,"); + selectFieldsSqlBuilder.append("abu.username as approvedByUsername,"); + selectFieldsSqlBuilder.append("abu.firstname as approvedByFirstname, abu.lastname as approvedByLastname,"); + selectFieldsSqlBuilder.append("sa.activatedon_date as activatedOnDate,"); + selectFieldsSqlBuilder.append("avbu.username as activatedByUsername,"); + selectFieldsSqlBuilder.append("avbu.firstname as activatedByFirstname, avbu.lastname as activatedByLastname,"); + selectFieldsSqlBuilder.append("sa.closedon_date as closedOnDate,"); + selectFieldsSqlBuilder.append("cbu.username as closedByUsername,"); + selectFieldsSqlBuilder.append("cbu.firstname as closedByFirstname, cbu.lastname as closedByLastname,"); + selectFieldsSqlBuilder + .append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMultiplesOf, "); + selectFieldsSqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); + selectFieldsSqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); + selectFieldsSqlBuilder.append("sa.nominal_annual_interest_rate as nominalAnnualInterestRate, "); + selectFieldsSqlBuilder.append("sa.interest_compounding_period_enum as interestCompoundingPeriodType, "); + selectFieldsSqlBuilder.append("sa.interest_posting_period_enum as interestPostingPeriodType, "); + selectFieldsSqlBuilder.append("sa.interest_calculation_type_enum as interestCalculationType, "); + selectFieldsSqlBuilder.append("sa.interest_calculation_days_in_year_type_enum as interestCalculationDaysInYearType, "); + selectFieldsSqlBuilder.append("sa.min_required_opening_balance as minRequiredOpeningBalance, "); + selectFieldsSqlBuilder.append("sa.lockin_period_frequency as lockinPeriodFrequency,"); + selectFieldsSqlBuilder.append("sa.lockin_period_frequency_enum as lockinPeriodFrequencyType, "); + selectFieldsSqlBuilder.append("sa.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, "); + selectFieldsSqlBuilder.append("sa.total_deposits_derived as totalDeposits, "); + selectFieldsSqlBuilder.append("sa.total_withdrawals_derived as totalWithdrawals, "); + selectFieldsSqlBuilder.append("sa.total_withdrawal_fees_derived as totalWithdrawalFees, "); + selectFieldsSqlBuilder.append("sa.total_annual_fees_derived as totalAnnualFees, "); + selectFieldsSqlBuilder.append("sa.total_interest_earned_derived as totalInterestEarned, "); + selectFieldsSqlBuilder.append("sa.total_interest_posted_derived as totalInterestPosted, "); + selectFieldsSqlBuilder.append("sa.account_balance_derived as accountBalance, "); + selectFieldsSqlBuilder.append("sa.total_fees_charge_derived as totalFeeCharge, "); + selectFieldsSqlBuilder.append("sa.total_penalty_charge_derived as totalPenaltyCharge, "); + selectFieldsSqlBuilder.append("sa.deposit_type_enum as depositTypeId "); + + this.selectFieldsSql = selectFieldsSqlBuilder.toString(); + + final StringBuilder selectTablesSqlBuilder = new StringBuilder(400); + selectTablesSqlBuilder.append("from m_savings_account sa "); + selectTablesSqlBuilder.append("left join m_deposit_account_term_and_preclosure datp on sa.id = datp.savings_account_id "); + selectTablesSqlBuilder.append("join m_savings_product sp ON sa.product_id = sp.id "); + selectTablesSqlBuilder.append("join m_currency curr on curr.code = sa.currency_code "); + selectTablesSqlBuilder.append("left join m_client c ON c.id = sa.client_id "); + selectTablesSqlBuilder.append("left join m_group g ON g.id = sa.group_id "); + selectTablesSqlBuilder.append("left join m_staff s ON s.id = sa.field_officer_id "); + selectTablesSqlBuilder.append("left join m_appuser sbu on sbu.id = sa.submittedon_userid "); + selectTablesSqlBuilder.append("left join m_appuser rbu on rbu.id = sa.rejectedon_userid "); + selectTablesSqlBuilder.append("left join m_appuser wbu on wbu.id = sa.withdrawnon_userid "); + selectTablesSqlBuilder.append("left join m_appuser abu on abu.id = sa.approvedon_userid "); + selectTablesSqlBuilder.append("left join m_appuser avbu on rbu.id = sa.activatedon_userid "); + selectTablesSqlBuilder.append("left join m_appuser cbu on cbu.id = sa.closedon_userid "); + + this.selectTablesSql = selectTablesSqlBuilder.toString(); + } + + public String selectFieldsSql() { + return this.selectFieldsSql; + } + + public String selectTablesSql() { + return this.selectTablesSql; + } + + public abstract String schema(); + + public DepositAccountData mapRow(final ResultSet rs) throws SQLException { + + final Long id = rs.getLong("id"); + final String accountNo = rs.getString("accountNo"); + final String externalId = rs.getString("externalId"); + + final Long groupId = JdbcSupport.getLong(rs, "groupId"); + final String groupName = rs.getString("groupName"); + final Long clientId = JdbcSupport.getLong(rs, "clientId"); + final String clientName = rs.getString("clientName"); + + final Long productId = rs.getLong("productId"); + final String productName = rs.getString("productName"); + + final Long fieldOfficerId = rs.getLong("fieldOfficerId"); + final String fieldOfficerName = rs.getString("fieldOfficerName"); + + final Integer statusEnum = JdbcSupport.getInteger(rs, "statusEnum"); + final SavingsAccountStatusEnumData status = SavingsEnumerations.status(statusEnum); + + final LocalDate submittedOnDate = JdbcSupport.getLocalDate(rs, "submittedOnDate"); + final String submittedByUsername = rs.getString("submittedByUsername"); + final String submittedByFirstname = rs.getString("submittedByFirstname"); + final String submittedByLastname = rs.getString("submittedByLastname"); + + final LocalDate rejectedOnDate = JdbcSupport.getLocalDate(rs, "rejectedOnDate"); + final String rejectedByUsername = rs.getString("rejectedByUsername"); + final String rejectedByFirstname = rs.getString("rejectedByFirstname"); + final String rejectedByLastname = rs.getString("rejectedByLastname"); + + final LocalDate withdrawnOnDate = JdbcSupport.getLocalDate(rs, "withdrawnOnDate"); + final String withdrawnByUsername = rs.getString("withdrawnByUsername"); + final String withdrawnByFirstname = rs.getString("withdrawnByFirstname"); + final String withdrawnByLastname = rs.getString("withdrawnByLastname"); + + final LocalDate approvedOnDate = JdbcSupport.getLocalDate(rs, "approvedOnDate"); + final String approvedByUsername = rs.getString("approvedByUsername"); + final String approvedByFirstname = rs.getString("approvedByFirstname"); + final String approvedByLastname = rs.getString("approvedByLastname"); + + final LocalDate activatedOnDate = JdbcSupport.getLocalDate(rs, "activatedOnDate"); + final String activatedByUsername = rs.getString("activatedByUsername"); + final String activatedByFirstname = rs.getString("activatedByFirstname"); + final String activatedByLastname = rs.getString("activatedByLastname"); + + final LocalDate closedOnDate = JdbcSupport.getLocalDate(rs, "closedOnDate"); + final String closedByUsername = rs.getString("closedByUsername"); + final String closedByFirstname = rs.getString("closedByFirstname"); + final String closedByLastname = rs.getString("closedByLastname"); + + final SavingsAccountApplicationTimelineData timeline = new SavingsAccountApplicationTimelineData(submittedOnDate, + submittedByUsername, submittedByFirstname, submittedByLastname, rejectedOnDate, rejectedByUsername, + rejectedByFirstname, rejectedByLastname, withdrawnOnDate, withdrawnByUsername, withdrawnByFirstname, + withdrawnByLastname, approvedOnDate, approvedByUsername, approvedByFirstname, approvedByLastname, activatedOnDate, + activatedByUsername, activatedByFirstname, activatedByLastname, closedOnDate, closedByUsername, closedByFirstname, + closedByLastname); + + final Integer depositTypeId = JdbcSupport.getInteger(rs, "depositTypeId"); + final EnumOptionData depositType = (depositTypeId == null) ? null : SavingsEnumerations.depositType(depositTypeId); + + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, + currencyDisplaySymbol, currencyNameCode); + + final BigDecimal nominalAnnualInterestRate = rs.getBigDecimal("nominalAnnualInterestRate"); + + final EnumOptionData interestCompoundingPeriodType = SavingsEnumerations + .compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.fromInt(JdbcSupport.getInteger(rs, + "interestCompoundingPeriodType"))); + + final EnumOptionData interestPostingPeriodType = SavingsEnumerations.interestPostingPeriodType(SavingsPostingInterestPeriodType + .fromInt(JdbcSupport.getInteger(rs, "interestPostingPeriodType"))); + + final EnumOptionData interestCalculationType = SavingsEnumerations.interestCalculationType(SavingsInterestCalculationType + .fromInt(JdbcSupport.getInteger(rs, "interestCalculationType"))); + + final EnumOptionData interestCalculationDaysInYearType = SavingsEnumerations + .interestCalculationDaysInYearType(SavingsInterestCalculationDaysInYearType.fromInt(JdbcSupport.getInteger(rs, + "interestCalculationDaysInYearType"))); + + final BigDecimal minRequiredOpeningBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredOpeningBalance"); + + final Integer lockinPeriodFrequency = JdbcSupport.getInteger(rs, "lockinPeriodFrequency"); + EnumOptionData lockinPeriodFrequencyType = null; + final Integer lockinPeriodFrequencyTypeValue = JdbcSupport.getInteger(rs, "lockinPeriodFrequencyType"); + if (lockinPeriodFrequencyTypeValue != null) { + final SavingsPeriodFrequencyType lockinPeriodType = SavingsPeriodFrequencyType.fromInt(lockinPeriodFrequencyTypeValue); + lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(lockinPeriodType); + } + + final boolean withdrawalFeeForTransfers = rs.getBoolean("withdrawalFeeForTransfers"); + + final BigDecimal totalDeposits = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalDeposits"); + final BigDecimal totalWithdrawals = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalWithdrawals"); + final BigDecimal totalWithdrawalFees = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalWithdrawalFees"); + final BigDecimal totalAnnualFees = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalAnnualFees"); + + final BigDecimal totalInterestEarned = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalInterestEarned"); + final BigDecimal totalInterestPosted = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalInterestPosted"); + final BigDecimal accountBalance = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "accountBalance"); + final BigDecimal totalFeeCharge = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalFeeCharge"); + final BigDecimal totalPenaltyCharge = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalPenaltyCharge"); + + final SavingsAccountSummaryData summary = new SavingsAccountSummaryData(currency, totalDeposits, totalWithdrawals, + totalWithdrawalFees, totalAnnualFees, totalInterestEarned, totalInterestPosted, accountBalance, totalFeeCharge, + totalPenaltyCharge); + + return DepositAccountData.instance(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, + fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, + interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, depositType); + } + } + + private static class FixedDepositAccountMapper extends DepositAccountMapper { + + private final String schemaSql; + + public FixedDepositAccountMapper() { + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append(super.selectFieldsSql()); + + sqlBuilder.append(", datp.interest_free_period_applicable as interestFreePeriodApplicable, "); + sqlBuilder.append("datp.interest_free_from_period as interestFreeFromPeriod, "); + sqlBuilder.append("datp.interest_free_to_period as interestFreeToPeriod, "); + sqlBuilder.append("datp.interest_free_period_frequency_enum as interestFreePeriodFrequencyId, "); + sqlBuilder.append("datp.pre_closure_penal_applicable as preClosurePenalApplicable, "); + sqlBuilder.append("datp.pre_closure_penal_interest as preClosurePenalInterest, "); + sqlBuilder.append("datp.pre_closure_penal_interest_on_enum as preClosurePenalInterestOnId, "); + sqlBuilder.append("datp.min_deposit_term as minDepositTerm, "); + sqlBuilder.append("datp.max_deposit_term as maxDepositTerm, "); + sqlBuilder.append("datp.min_deposit_term_type_enum as minDepositTermTypeId, "); + sqlBuilder.append("datp.max_deposit_term_type_enum as maxDepositTermTypeId, "); + sqlBuilder.append("datp.in_multiples_of_deposit_term as inMultiplesOfDepositTerm, "); + sqlBuilder.append("datp.in_multiples_of_deposit_term_type_enum as inMultiplesOfDepositTermTypeId, "); + sqlBuilder.append("datp.deposit_amount as depositAmount, "); + sqlBuilder.append("datp.maturity_amount as maturityAmount, "); + sqlBuilder.append("datp.maturity_date as maturityDate, "); + sqlBuilder.append("datp.deposit_period as depositPeriod, "); + sqlBuilder.append("datp.deposit_period_frequency_enum as depositPeriodFrequencyTypeId, "); + sqlBuilder.append("datp.on_account_closure_enum as onAccountClosureId "); + + sqlBuilder.append(super.selectTablesSql()); + + this.schemaSql = sqlBuilder.toString(); + } + + @Override + public String schema() { + return this.schemaSql; + } + + @Override + public FixedDepositAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final DepositAccountData depositAccountData = super.mapRow(rs); + + final boolean interestFreePeriodApplicable = rs.getBoolean("interestFreePeriodApplicable"); + final Integer interestFreeFromPeriod = JdbcSupport.getInteger(rs, "interestFreeFromPeriod"); + final Integer interestFreeToPeriod = JdbcSupport.getInteger(rs, "interestFreeToPeriod"); + final Integer periodFrequencyTypeId = JdbcSupport.getInteger(rs, "interestFreePeriodFrequencyId"); + final EnumOptionData interestFreePeriodFrequencyType = (periodFrequencyTypeId == null) ? null : InterestRateChartEnumerations + .periodType(periodFrequencyTypeId); + final boolean preClosurePenalApplicable = rs.getBoolean("preClosurePenalApplicable"); + final BigDecimal preClosurePenalInterest = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "preClosurePenalInterest"); + final Integer preClosurePenalInterestOnTypeId = JdbcSupport.getInteger(rs, "preClosurePenalInterestOnId"); + final EnumOptionData preClosurePenalInterestOnType = (preClosurePenalInterestOnTypeId == null) ? null : SavingsEnumerations + .preClosurePenaltyInterestOnType(preClosurePenalInterestOnTypeId); + + final Integer minDepositTerm = JdbcSupport.getInteger(rs, "minDepositTerm"); + final Integer maxDepositTerm = JdbcSupport.getInteger(rs, "maxDepositTerm"); + final Integer minDepositTermTypeId = JdbcSupport.getInteger(rs, "minDepositTermTypeId"); + final EnumOptionData minDepositTermType = (minDepositTermTypeId == null) ? null : SavingsEnumerations + .depositTermFrequencyType(minDepositTermTypeId); + final Integer maxDepositTermTypeId = JdbcSupport.getInteger(rs, "maxDepositTermTypeId"); + final EnumOptionData maxDepositTermType = (maxDepositTermTypeId == null) ? null : SavingsEnumerations + .depositTermFrequencyType(maxDepositTermTypeId); + final Integer inMultiplesOfDepositTerm = JdbcSupport.getInteger(rs, "inMultiplesOfDepositTerm"); + final Integer inMultiplesOfDepositTermTypeId = JdbcSupport.getInteger(rs, "inMultiplesOfDepositTermTypeId"); + final EnumOptionData inMultiplesOfDepositTermType = (inMultiplesOfDepositTermTypeId == null) ? null : SavingsEnumerations + .depositTermFrequencyType(inMultiplesOfDepositTermTypeId); + + final BigDecimal depositAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "depositAmount"); + final BigDecimal maturityAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "maturityAmount"); + final LocalDate maturityDate = JdbcSupport.getLocalDate(rs, "maturityDate"); + final Integer depositPeriod = JdbcSupport.getInteger(rs, "depositPeriod"); + final Integer depositPeriodFrequencyTypeId = JdbcSupport.getInteger(rs, "depositPeriodFrequencyTypeId"); + final EnumOptionData depositPeriodFrequencyType = (depositPeriodFrequencyTypeId == null) ? null : SavingsEnumerations + .depositPeriodFrequency(depositPeriodFrequencyTypeId); + + final Integer onAccountClosureId = JdbcSupport.getInteger(rs, "onAccountClosureId"); + final EnumOptionData onAccountClosureType = (onAccountClosureId == null) ? null : SavingsEnumerations + .depositAccountOnClosureType(onAccountClosureId); + + return FixedDepositAccountData.instance(depositAccountData, interestFreePeriodApplicable, interestFreeFromPeriod, + interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest, + preClosurePenalInterestOnType, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, + inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod, + depositPeriodFrequencyType, onAccountClosureType); + } + } + + private static class RecurringDepositAccountMapper extends DepositAccountMapper { + + private final String schemaSql; + + public RecurringDepositAccountMapper() { + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append(this.selectFieldsSql()); + + sqlBuilder.append(", datp.interest_free_period_applicable as interestFreePeriodApplicable, "); + sqlBuilder.append("datp.interest_free_from_period as interestFreeFromPeriod, "); + sqlBuilder.append("datp.interest_free_to_period as interestFreeToPeriod, "); + sqlBuilder.append("datp.interest_free_period_frequency_enum as interestFreePeriodFrequencyId, "); + sqlBuilder.append("datp.pre_closure_penal_applicable as preClosurePenalApplicable, "); + sqlBuilder.append("datp.pre_closure_penal_interest as preClosurePenalInterest, "); + sqlBuilder.append("datp.pre_closure_penal_interest_on_enum as preClosurePenalInterestOnId, "); + sqlBuilder.append("dard.recurring_deposit_amount as recurringDepositAmount, "); + sqlBuilder.append("dard.recurring_deposit_type_enum as recurringDepositTypeId, "); + sqlBuilder.append("dard.recurring_deposit_frequency as recurringDepositFrequency, "); + sqlBuilder.append("dard.recurring_deposit_frequency_type_enum as recurringDepositFrequencyTypeId, "); + sqlBuilder.append("datp.min_deposit_term as minDepositTerm, "); + sqlBuilder.append("datp.max_deposit_term as maxDepositTerm, "); + sqlBuilder.append("datp.min_deposit_term_type_enum as minDepositTermTypeId, "); + sqlBuilder.append("datp.max_deposit_term_type_enum as maxDepositTermTypeId, "); + sqlBuilder.append("datp.in_multiples_of_deposit_term as inMultiplesOfDepositTerm, "); + sqlBuilder.append("datp.in_multiples_of_deposit_term_type_enum as inMultiplesOfDepositTermTypeId, "); + sqlBuilder.append("datp.deposit_amount as depositAmount, "); + sqlBuilder.append("datp.maturity_amount as maturityAmount, "); + sqlBuilder.append("datp.maturity_date as maturityDate, "); + sqlBuilder.append("datp.deposit_period as depositPeriod, "); + sqlBuilder.append("datp.deposit_period_frequency_enum as depositPeriodFrequencyTypeId, "); + sqlBuilder.append("datp.on_account_closure_enum as onAccountClosureId "); + + sqlBuilder.append(this.selectTablesSql()); + sqlBuilder.append("left join m_deposit_account_recurring_detail dard on sa.id = dard.savings_account_id "); + + this.schemaSql = sqlBuilder.toString(); + } + + @Override + public String schema() { + return this.schemaSql; + } + + @Override + public RecurringDepositAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final DepositAccountData depositAccountData = super.mapRow(rs); + + final boolean interestFreePeriodApplicable = rs.getBoolean("interestFreePeriodApplicable"); + final Integer interestFreeFromPeriod = JdbcSupport.getInteger(rs, "interestFreeFromPeriod"); + final Integer interestFreeToPeriod = JdbcSupport.getInteger(rs, "interestFreeToPeriod"); + final Integer periodFrequencyTypeId = JdbcSupport.getInteger(rs, "interestFreePeriodFrequencyId"); + final EnumOptionData interestFreePeriodFrequencyType = (periodFrequencyTypeId == null) ? null : InterestRateChartEnumerations + .periodType(periodFrequencyTypeId); + final boolean preClosurePenalApplicable = rs.getBoolean("preClosurePenalApplicable"); + final BigDecimal preClosurePenalInterest = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "preClosurePenalInterest"); + final Integer preClosurePenalInterestOnTypeId = JdbcSupport.getInteger(rs, "preClosurePenalInterestOnId"); + final EnumOptionData preClosurePenalInterestOnType = (preClosurePenalInterestOnTypeId == null) ? null : SavingsEnumerations + .preClosurePenaltyInterestOnType(preClosurePenalInterestOnTypeId); + final Integer recurringDepositTypeId = JdbcSupport.getInteger(rs, "recurringDepositTypeId"); + final EnumOptionData recurringDepositType = (recurringDepositTypeId == null) ? null : SavingsEnumerations + .recurringDepositType(recurringDepositTypeId); + final Integer recurringDepositFrequency = JdbcSupport.getInteger(rs, "recurringDepositFrequency"); + final Integer recurringDepositFrequencyTypeId = JdbcSupport.getInteger(rs, "recurringDepositFrequencyTypeId"); + final EnumOptionData recurringDepositFrequencyType = (recurringDepositFrequencyTypeId == null) ? null : SavingsEnumerations + .recurringDepositFrequencyType(recurringDepositFrequencyTypeId); + + final Integer minDepositTerm = JdbcSupport.getInteger(rs, "minDepositTerm"); + final Integer maxDepositTerm = JdbcSupport.getInteger(rs, "maxDepositTerm"); + final Integer minDepositTermTypeId = JdbcSupport.getInteger(rs, "minDepositTermTypeId"); + final EnumOptionData minDepositTermType = (minDepositTermTypeId == null) ? null : SavingsEnumerations + .depositTermFrequencyType(minDepositTermTypeId); + final Integer maxDepositTermTypeId = JdbcSupport.getInteger(rs, "maxDepositTermTypeId"); + final EnumOptionData maxDepositTermType = (maxDepositTermTypeId == null) ? null : SavingsEnumerations + .depositTermFrequencyType(maxDepositTermTypeId); + final Integer inMultiplesOfDepositTerm = JdbcSupport.getInteger(rs, "inMultiplesOfDepositTerm"); + final Integer inMultiplesOfDepositTermTypeId = JdbcSupport.getInteger(rs, "inMultiplesOfDepositTermTypeId"); + final EnumOptionData inMultiplesOfDepositTermType = (inMultiplesOfDepositTermTypeId == null) ? null : SavingsEnumerations + .depositTermFrequencyType(inMultiplesOfDepositTermTypeId); + + final BigDecimal depositAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "depositAmount"); + final BigDecimal maturityAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "maturityAmount"); + final LocalDate maturityDate = JdbcSupport.getLocalDate(rs, "maturityDate"); + final Integer depositPeriod = JdbcSupport.getInteger(rs, "depositPeriod"); + final Integer depositPeriodFrequencyTypeId = JdbcSupport.getInteger(rs, "depositPeriodFrequencyTypeId"); + final EnumOptionData depositPeriodFrequencyType = (depositPeriodFrequencyTypeId == null) ? null : SavingsEnumerations + .depositPeriodFrequency(depositPeriodFrequencyTypeId); + final BigDecimal recurringDepositAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "recurringDepositAmount"); + final Integer onAccountClosureId = JdbcSupport.getInteger(rs, "onAccountClosureId"); + final EnumOptionData onAccountClosureType = (onAccountClosureId == null) ? null : SavingsEnumerations + .depositAccountOnClosureType(onAccountClosureId); + return RecurringDepositAccountData.instance(depositAccountData, interestFreePeriodApplicable, interestFreeFromPeriod, + interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest, + preClosurePenalInterestOnType, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, + inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod, + depositPeriodFrequencyType, recurringDepositAmount, recurringDepositType, recurringDepositFrequency, + recurringDepositFrequencyType, onAccountClosureType); + } + } + + private static final class DepositAccountLookupMapper implements RowMapper { + + public String schema() { + return " sa.id as id, sa.account_no as accountNumber, sa.deposit_type_enum as depositTypeId from m_savings_account sa "; + } + + @Override + public DepositAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = rs.getLong("id"); + final String name = rs.getString("accountNumber"); + final Integer depositTypeId = JdbcSupport.getInteger(rs, "depositTypeId"); + final EnumOptionData depositType = (depositTypeId == null) ? null : SavingsEnumerations.depositType(depositTypeId); + + return DepositAccountData.lookup(id, name, depositType); + } + } + + private DepositAccountMapper getDepositAccountMapper(final DepositAccountType depositAccountType) { + if (depositAccountType.isFixedDeposit()) { + return this.fixedDepositAccountRowMapper; + } else if (depositAccountType.isRecurringDeposit()) { return this.recurringDepositAccountRowMapper; } + return null; + } + + private static final class SavingsAccountTransactionsMapper implements RowMapper { + + private final String schemaSql; + + public SavingsAccountTransactionsMapper() { + + final StringBuilder sqlBuilder = new StringBuilder(400); + 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.running_balance_derived as runningBalance, tr.is_reversed as reversed,"); + sqlBuilder.append("fromtran.id as fromTransferId, fromtran.is_reversed as fromTransferReversed,"); + sqlBuilder.append("fromtran.transaction_date as fromTransferDate, fromtran.amount as fromTransferAmount,"); + sqlBuilder.append("fromtran.description as fromTransferDescription,"); + sqlBuilder.append("totran.id as toTransferId, totran.is_reversed as toTransferReversed,"); + sqlBuilder.append("totran.transaction_date as toTransferDate, totran.amount as toTransferAmount,"); + sqlBuilder.append("totran.description as toTransferDescription,"); + sqlBuilder.append("sa.id as savingsId, sa.account_no as accountNo,"); + sqlBuilder.append("pd.payment_type_cv_id as paymentType,pd.account_number as accountNumber,pd.check_number as checkNumber, "); + sqlBuilder.append("pd.receipt_number as receiptNumber, pd.bank_number as bankNumber,pd.routing_code as routingCode, "); + sqlBuilder + .append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMultiplesOf, "); + sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); + sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); + sqlBuilder.append("cv.code_value as paymentTypeName "); + sqlBuilder.append("from m_savings_account sa "); + sqlBuilder.append("join m_savings_account_transaction tr on tr.savings_account_id = sa.id "); + sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code "); + sqlBuilder.append("left join m_account_transfer_transaction fromtran on fromtran.from_savings_transaction_id = tr.id "); + sqlBuilder.append("left join m_account_transfer_transaction totran on totran.to_savings_transaction_id = tr.id "); + sqlBuilder.append("left join m_payment_detail pd on tr.payment_detail_id = pd.id "); + sqlBuilder.append("left join m_code_value cv on pd.payment_type_cv_id = cv.id "); + + this.schemaSql = sqlBuilder.toString(); + } + + public String schema() { + return this.schemaSql; + } + + @Override + public SavingsAccountTransactionData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + final Long id = rs.getLong("transactionId"); + final int transactionTypeInt = JdbcSupport.getInteger(rs, "transactionType"); + final SavingsAccountTransactionEnumData transactionType = SavingsEnumerations.transactionType(transactionTypeInt); + + final LocalDate date = JdbcSupport.getLocalDate(rs, "transactionDate"); + final BigDecimal amount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "transactionAmount"); + final BigDecimal runningBalance = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "runningBalance"); + final boolean reversed = rs.getBoolean("reversed"); + + final Long savingsId = rs.getLong("savingsId"); + final String accountNo = rs.getString("accountNo"); + + PaymentDetailData paymentDetailData = null; + if (transactionType.isDepositOrWithdrawal()) { + final Long paymentTypeId = JdbcSupport.getLong(rs, "paymentType"); + if (paymentTypeId != null) { + final String typeName = rs.getString("paymentTypeName"); + final CodeValueData paymentType = CodeValueData.instance(paymentTypeId, typeName); + final String accountNumber = rs.getString("accountNumber"); + final String checkNumber = rs.getString("checkNumber"); + final String routingCode = rs.getString("routingCode"); + final String receiptNumber = rs.getString("receiptNumber"); + final String bankNumber = rs.getString("bankNumber"); + paymentDetailData = new PaymentDetailData(id, paymentType, accountNumber, checkNumber, routingCode, receiptNumber, + bankNumber); + } + } + + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, + currencyDisplaySymbol, currencyNameCode); + + AccountTransferData transfer = null; + final Long fromTransferId = JdbcSupport.getLong(rs, "fromTransferId"); + final Long toTransferId = JdbcSupport.getLong(rs, "toTransferId"); + if (fromTransferId != null) { + final LocalDate fromTransferDate = JdbcSupport.getLocalDate(rs, "fromTransferDate"); + final BigDecimal fromTransferAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "fromTransferAmount"); + final boolean fromTransferReversed = rs.getBoolean("fromTransferReversed"); + final String fromTransferDescription = rs.getString("fromTransferDescription"); + + transfer = AccountTransferData.transferBasicDetails(fromTransferId, currency, fromTransferAmount, fromTransferDate, + fromTransferDescription, fromTransferReversed); + } else if (toTransferId != null) { + final LocalDate toTransferDate = JdbcSupport.getLocalDate(rs, "toTransferDate"); + final BigDecimal toTransferAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "toTransferAmount"); + final boolean toTransferReversed = rs.getBoolean("toTransferReversed"); + final String toTransferDescription = rs.getString("toTransferDescription"); + + transfer = AccountTransferData.transferBasicDetails(toTransferId, currency, toTransferAmount, toTransferDate, + toTransferDescription, toTransferReversed); + } + + return SavingsAccountTransactionData.create(id, transactionType, paymentDetailData, savingsId, accountNo, date, currency, + amount, runningBalance, reversed, transfer); + } + } + + private static abstract class DepositAccountTemplateMapper implements RowMapper { + + private final String selectFieldsSql; + private final String selectTablesSql; + + private final ClientData client; + private final GroupGeneralData group; + + public DepositAccountTemplateMapper(final ClientData client, final GroupGeneralData group) { + this.client = client; + this.group = group; + + final StringBuilder selectFieldsSqlBuilder = new StringBuilder(400); + selectFieldsSqlBuilder.append("sa.id as productId, sa.name as productName, "); + selectFieldsSqlBuilder + .append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMultiplesOf, "); + selectFieldsSqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); + selectFieldsSqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); + selectFieldsSqlBuilder.append("sa.nominal_annual_interest_rate as nominalAnnualIterestRate, "); + selectFieldsSqlBuilder.append("sa.interest_compounding_period_enum as interestCompoundingPeriodType, "); + selectFieldsSqlBuilder.append("sa.interest_posting_period_enum as interestPostingPeriodType, "); + selectFieldsSqlBuilder.append("sa.interest_calculation_type_enum as interestCalculationType, "); + selectFieldsSqlBuilder.append("sa.interest_calculation_days_in_year_type_enum as interestCalculationDaysInYearType, "); + selectFieldsSqlBuilder.append("sa.min_required_opening_balance as minRequiredOpeningBalance, "); + selectFieldsSqlBuilder.append("sa.lockin_period_frequency as lockinPeriodFrequency,"); + selectFieldsSqlBuilder.append("sa.lockin_period_frequency_enum as lockinPeriodFrequencyType, "); + selectFieldsSqlBuilder.append("sa.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, "); + selectFieldsSqlBuilder.append("sa.deposit_type_enum as depositTypeId "); + + this.selectFieldsSql = selectFieldsSqlBuilder.toString(); + + final StringBuilder selectTablesSqlBuilder = new StringBuilder(400); + selectTablesSqlBuilder.append("from m_savings_product sa "); + selectTablesSqlBuilder.append("left join m_deposit_product_term_and_preclosure dptp on sa.id = dptp.savings_product_id "); + selectTablesSqlBuilder.append("join m_currency curr on curr.code = sa.currency_code "); + + this.selectTablesSql = selectTablesSqlBuilder.toString(); + } + + public String selectFieldsSql() { + return this.selectFieldsSql; + } + + public String selectTablesSql() { + return this.selectTablesSql; + } + + public abstract String schema(); + + @Override + public abstract DepositAccountData mapRow(final ResultSet rs, final int rowNum) throws SQLException; + + public DepositAccountData mapRow(final ResultSet rs) throws SQLException { + + final Integer depositTypeId = JdbcSupport.getInteger(rs, "depositTypeId"); + final EnumOptionData depositType = (depositTypeId == null) ? null : SavingsEnumerations.depositType(depositTypeId); + final Long productId = rs.getLong("productId"); + final String productName = rs.getString("productName"); + + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, + currencyDisplaySymbol, currencyNameCode); + + final BigDecimal nominalAnnualIterestRate = rs.getBigDecimal("nominalAnnualIterestRate"); + + final EnumOptionData interestCompoundingPeriodType = SavingsEnumerations + .compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.fromInt(JdbcSupport.getInteger(rs, + "interestCompoundingPeriodType"))); + + final EnumOptionData interestPostingPeriodType = SavingsEnumerations.interestPostingPeriodType(SavingsPostingInterestPeriodType + .fromInt(JdbcSupport.getInteger(rs, "interestPostingPeriodType"))); + + final EnumOptionData interestCalculationType = SavingsEnumerations.interestCalculationType(SavingsInterestCalculationType + .fromInt(JdbcSupport.getInteger(rs, "interestCalculationType"))); + + final EnumOptionData interestCalculationDaysInYearType = SavingsEnumerations + .interestCalculationDaysInYearType(SavingsInterestCalculationDaysInYearType.fromInt(JdbcSupport.getInteger(rs, + "interestCalculationDaysInYearType"))); + + final BigDecimal minRequiredOpeningBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredOpeningBalance"); + + final Integer lockinPeriodFrequency = JdbcSupport.getInteger(rs, "lockinPeriodFrequency"); + EnumOptionData lockinPeriodFrequencyType = null; + final Integer lockinPeriodFrequencyTypeValue = JdbcSupport.getInteger(rs, "lockinPeriodFrequencyType"); + if (lockinPeriodFrequencyTypeValue != null) { + final SavingsPeriodFrequencyType lockinPeriodType = SavingsPeriodFrequencyType.fromInt(lockinPeriodFrequencyTypeValue); + lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(lockinPeriodType); + } + + final boolean withdrawalFeeForTransfers = rs.getBoolean("withdrawalFeeForTransfers"); + + Long clientId = null; + String clientName = null; + if (this.client != null) { + clientId = this.client.id(); + clientName = this.client.displayName(); + } + + Long groupId = null; + String groupName = null; + if (this.group != null) { + groupId = this.group.getId(); + groupName = this.group.getName(); + } + + final Long fieldOfficerId = null; + final String fieldOfficerName = null; + final SavingsAccountStatusEnumData status = null; + final SavingsAccountSummaryData summary = null; + final SavingsAccountApplicationTimelineData timeline = SavingsAccountApplicationTimelineData.templateDefault(); + + return DepositAccountData.instance(null, null, null, groupId, groupName, clientId, clientName, productId, productName, + fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualIterestRate, interestCompoundingPeriodType, + interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, depositType); + } + } + + private static class FixedDepositAccountTemplateMapper extends DepositAccountTemplateMapper { + + private final String schemaSql; + + public FixedDepositAccountTemplateMapper(final ClientData client, final GroupGeneralData group) { + super(client, group); + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append(super.selectFieldsSql()); + + sqlBuilder.append(", dptp.interest_free_period_applicable as interestFreePeriodApplicable, "); + sqlBuilder.append("dptp.interest_free_from_period as interestFreeFromPeriod, "); + sqlBuilder.append("dptp.interest_free_to_period as interestFreeToPeriod, "); + sqlBuilder.append("dptp.interest_free_period_frequency_enum as interestFreePeriodFrequencyId, "); + sqlBuilder.append("dptp.pre_closure_penal_applicable as preClosurePenalApplicable, "); + sqlBuilder.append("dptp.pre_closure_penal_interest as preClosurePenalInterest, "); + sqlBuilder.append("dptp.pre_closure_penal_interest_on_enum as preClosurePenalInterestOnId, "); + sqlBuilder.append("dptp.min_deposit_term as minDepositTerm, "); + sqlBuilder.append("dptp.max_deposit_term as maxDepositTerm, "); + sqlBuilder.append("dptp.min_deposit_term_type_enum as minDepositTermTypeId, "); + sqlBuilder.append("dptp.max_deposit_term_type_enum as maxDepositTermTypeId, "); + sqlBuilder.append("dptp.in_multiples_of_deposit_term as inMultiplesOfDepositTerm, "); + sqlBuilder.append("dptp.in_multiples_of_deposit_term_type_enum as inMultiplesOfDepositTermTypeId "); + + sqlBuilder.append(super.selectTablesSql()); + + this.schemaSql = sqlBuilder.toString(); + } + + @Override + public String schema() { + return this.schemaSql; + } + + @Override + public FixedDepositAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final DepositAccountData depositAccountData = super.mapRow(rs); + + final boolean interestFreePeriodApplicable = rs.getBoolean("interestFreePeriodApplicable"); + final Integer interestFreeFromPeriod = JdbcSupport.getInteger(rs, "interestFreeFromPeriod"); + final Integer interestFreeToPeriod = JdbcSupport.getInteger(rs, "interestFreeToPeriod"); + final Integer periodFrequencyTypeId = JdbcSupport.getInteger(rs, "interestFreePeriodFrequencyId"); + final EnumOptionData interestFreePeriodFrequencyType = (periodFrequencyTypeId == null) ? null : InterestRateChartEnumerations + .periodType(periodFrequencyTypeId); + final boolean preClosurePenalApplicable = rs.getBoolean("preClosurePenalApplicable"); + final BigDecimal preClosurePenalInterest = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "preClosurePenalInterest"); + final Integer preClosurePenalInterestOnTypeId = JdbcSupport.getInteger(rs, "preClosurePenalInterestOnId"); + final EnumOptionData preClosurePenalInterestOnType = (preClosurePenalInterestOnTypeId == null) ? null : SavingsEnumerations + .preClosurePenaltyInterestOnType(preClosurePenalInterestOnTypeId); + + final Integer minDepositTerm = JdbcSupport.getInteger(rs, "minDepositTerm"); + final Integer maxDepositTerm = JdbcSupport.getInteger(rs, "maxDepositTerm"); + final Integer minDepositTermTypeId = JdbcSupport.getInteger(rs, "minDepositTermTypeId"); + final EnumOptionData minDepositTermType = (minDepositTermTypeId == null) ? null : SavingsEnumerations + .depositTermFrequencyType(minDepositTermTypeId); + final Integer maxDepositTermTypeId = JdbcSupport.getInteger(rs, "maxDepositTermTypeId"); + final EnumOptionData maxDepositTermType = (maxDepositTermTypeId == null) ? null : SavingsEnumerations + .depositTermFrequencyType(maxDepositTermTypeId); + final Integer inMultiplesOfDepositTerm = JdbcSupport.getInteger(rs, "inMultiplesOfDepositTerm"); + final Integer inMultiplesOfDepositTermTypeId = JdbcSupport.getInteger(rs, "inMultiplesOfDepositTermTypeId"); + final EnumOptionData inMultiplesOfDepositTermType = (inMultiplesOfDepositTermTypeId == null) ? null : SavingsEnumerations + .depositTermFrequencyType(inMultiplesOfDepositTermTypeId); + + final BigDecimal depositAmount = null; + final BigDecimal maturityAmount = null; + final LocalDate maturityDate = null; + final Integer depositPeriod = null; + final EnumOptionData depositPeriodFrequencyType = null; + final EnumOptionData onAccountClosureType = null; + + return FixedDepositAccountData.instance(depositAccountData, interestFreePeriodApplicable, interestFreeFromPeriod, + interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest, + preClosurePenalInterestOnType, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, + inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod, + depositPeriodFrequencyType, onAccountClosureType); + } + } + + private static class RecurringDepositAccountTemplateMapper extends DepositAccountTemplateMapper { + + private final String schemaSql; + + public RecurringDepositAccountTemplateMapper(final ClientData client, final GroupGeneralData group) { + super(client, group); + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append(this.selectFieldsSql()); + + sqlBuilder.append(", dptp.interest_free_period_applicable as interestFreePeriodApplicable, "); + sqlBuilder.append("dptp.interest_free_from_period as interestFreeFromPeriod, "); + sqlBuilder.append("dptp.interest_free_to_period as interestFreeToPeriod, "); + sqlBuilder.append("dptp.interest_free_period_frequency_enum as interestFreePeriodFrequencyId, "); + sqlBuilder.append("dptp.pre_closure_penal_applicable as preClosurePenalApplicable, "); + sqlBuilder.append("dptp.pre_closure_penal_interest as preClosurePenalInterest, "); + sqlBuilder.append("dptp.pre_closure_penal_interest_on_enum as preClosurePenalInterestOnId, "); + sqlBuilder.append("dprd.recurring_deposit_type_enum as recurringDepositTypeId, "); + sqlBuilder.append("dprd.recurring_deposit_frequency as recurringDepositFrequency, "); + sqlBuilder.append("dprd.recurring_deposit_frequency_type_enum as recurringDepositFrequencyTypeId, "); + sqlBuilder.append("dptp.min_deposit_term as minDepositTerm, "); + sqlBuilder.append("dptp.max_deposit_term as maxDepositTerm, "); + sqlBuilder.append("dptp.min_deposit_term_type_enum as minDepositTermTypeId, "); + sqlBuilder.append("dptp.max_deposit_term_type_enum as maxDepositTermTypeId, "); + sqlBuilder.append("dptp.in_multiples_of_deposit_term as inMultiplesOfDepositTerm, "); + sqlBuilder.append("dptp.in_multiples_of_deposit_term_type_enum as inMultiplesOfDepositTermTypeId "); + + sqlBuilder.append(this.selectTablesSql()); + sqlBuilder.append("left join m_deposit_product_recurring_detail dprd on sa.id = dprd.savings_product_id "); + + this.schemaSql = sqlBuilder.toString(); + } + + @Override + public String schema() { + return this.schemaSql; + } + + @Override + public RecurringDepositAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final DepositAccountData depositAccountData = super.mapRow(rs); + + final boolean interestFreePeriodApplicable = rs.getBoolean("interestFreePeriodApplicable"); + final Integer interestFreeFromPeriod = JdbcSupport.getInteger(rs, "interestFreeFromPeriod"); + final Integer interestFreeToPeriod = JdbcSupport.getInteger(rs, "interestFreeToPeriod"); + final Integer periodFrequencyTypeId = JdbcSupport.getInteger(rs, "interestFreePeriodFrequencyId"); + final EnumOptionData interestFreePeriodFrequencyType = (periodFrequencyTypeId == null) ? null : InterestRateChartEnumerations + .periodType(periodFrequencyTypeId); + final boolean preClosurePenalApplicable = rs.getBoolean("preClosurePenalApplicable"); + final BigDecimal preClosurePenalInterest = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "preClosurePenalInterest"); + final Integer preClosurePenalInterestOnTypeId = JdbcSupport.getInteger(rs, "preClosurePenalInterestOnId"); + final EnumOptionData preClosurePenalInterestOnType = (preClosurePenalInterestOnTypeId == null) ? null : SavingsEnumerations + .preClosurePenaltyInterestOnType(preClosurePenalInterestOnTypeId); + final Integer recurringDepositTypeId = JdbcSupport.getInteger(rs, "recurringDepositTypeId"); + final EnumOptionData recurringDepositType = (recurringDepositTypeId == null) ? null : SavingsEnumerations + .recurringDepositType(recurringDepositTypeId); + final Integer recurringDepositFrequency = JdbcSupport.getInteger(rs, "recurringDepositFrequency"); + final Integer recurringDepositFrequencyTypeId = JdbcSupport.getInteger(rs, "recurringDepositFrequencyTypeId"); + final EnumOptionData recurringDepositFrequencyType = (recurringDepositFrequencyTypeId == null) ? null : SavingsEnumerations + .recurringDepositFrequencyType(recurringDepositFrequencyTypeId); + + final Integer minDepositTerm = JdbcSupport.getInteger(rs, "minDepositTerm"); + final Integer maxDepositTerm = JdbcSupport.getInteger(rs, "maxDepositTerm"); + final Integer minDepositTermTypeId = JdbcSupport.getInteger(rs, "minDepositTermTypeId"); + final EnumOptionData minDepositTermType = (minDepositTermTypeId == null) ? null : SavingsEnumerations + .depositTermFrequencyType(minDepositTermTypeId); + final Integer maxDepositTermTypeId = JdbcSupport.getInteger(rs, "maxDepositTermTypeId"); + final EnumOptionData maxDepositTermType = (maxDepositTermTypeId == null) ? null : SavingsEnumerations + .depositTermFrequencyType(maxDepositTermTypeId); + final Integer inMultiplesOfDepositTerm = JdbcSupport.getInteger(rs, "inMultiplesOfDepositTerm"); + final Integer inMultiplesOfDepositTermTypeId = JdbcSupport.getInteger(rs, "inMultiplesOfDepositTermTypeId"); + final EnumOptionData inMultiplesOfDepositTermType = (inMultiplesOfDepositTermTypeId == null) ? null : SavingsEnumerations + .depositTermFrequencyType(inMultiplesOfDepositTermTypeId); + + final BigDecimal depositAmount = null; + final BigDecimal maturityAmount = null; + final LocalDate maturityDate = null; + final Integer depositPeriod = null; + final EnumOptionData depositPeriodFrequencyType = null; + final BigDecimal recurringDepositAmount = null; + final EnumOptionData onAccountClosureType = null; + return RecurringDepositAccountData.instance(depositAccountData, interestFreePeriodApplicable, interestFreeFromPeriod, + interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest, + preClosurePenalInterestOnType, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, + inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod, + depositPeriodFrequencyType, recurringDepositAmount, recurringDepositType, recurringDepositFrequency, + recurringDepositFrequencyType, onAccountClosureType); + } + } + + private DepositAccountTemplateMapper getDepositAccountTemplaMapper(final DepositAccountType depositAccountType, ClientData client, + GroupGeneralData group) { + if (depositAccountType.isFixedDeposit()) { + return new FixedDepositAccountTemplateMapper(client, group); + } else if (depositAccountType.isRecurringDeposit()) { return new RecurringDepositAccountTemplateMapper(client, group); } + return null; + } + + private Collection fromChargesToSavingsCharges(final Collection productCharges) { + final Collection savingsCharges = new ArrayList(); + for (final ChargeData chargeData : productCharges) { + final SavingsAccountChargeData savingsCharge = chargeData.toSavingsAccountChargeData(); + savingsCharges.add(savingsCharge); + } + return savingsCharges; + } + + private static final class DepositAccountForMaturityMapper implements RowMapper { + + private final String schemaSql; + + public DepositAccountForMaturityMapper() { + final StringBuilder sqlBuilder = new StringBuilder(200); + sqlBuilder.append("da.id as id, "); + sqlBuilder.append("da.account_no as accountNumber, "); + sqlBuilder.append("da.deposit_type_enum as depositTypeId "); + sqlBuilder.append("FROM m_savings_account da "); + + this.schemaSql = sqlBuilder.toString(); + } + + public String schema() { + return this.schemaSql; + } + + @Override + public DepositAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = rs.getLong("id"); + final String name = rs.getString("accountNumber"); + final Integer depositTypeId = JdbcSupport.getInteger(rs, "depositTypeId"); + final EnumOptionData depositType = (depositTypeId == null) ? null : SavingsEnumerations.depositType(depositTypeId); + + return DepositAccountData.lookup(id, name, depositType); + } + } + + private static final class RecurringAccountDepositTransactionTemplateMapper implements RowMapper { + + private final String schemaSql; + + public RecurringAccountDepositTransactionTemplateMapper() { + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("sa.id as id, sa.account_no as accountNo, "); + sqlBuilder + .append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMultiplesOf, "); + sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); + sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); + sqlBuilder.append("dard.recurring_deposit_amount as recurringDepositAmount "); + sqlBuilder.append("from m_savings_account sa "); + sqlBuilder.append("join m_deposit_account_recurring_detail dard on dard.savings_account_id = sa.id "); + sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code "); + + this.schemaSql = sqlBuilder.toString(); + } + + public String schema() { + return this.schemaSql; + } + + @Override + public SavingsAccountTransactionData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long savingsId = rs.getLong("id"); + final String accountNo = rs.getString("accountNo"); + final BigDecimal recurringDepositAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "recurringDepositAmount"); + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, + currencyDisplaySymbol, currencyNameCode); + final SavingsAccountTransactionEnumData transactionType = SavingsEnumerations + .transactionType(SavingsAccountTransactionType.DEPOSIT.getValue()); + final PaymentDetailData paymentDetailData = null; + final AccountTransferData transfer = null; + final BigDecimal runningBalance = null; + return SavingsAccountTransactionData.create(savingsId, transactionType, paymentDetailData, savingsId, accountNo, + DateUtils.getLocalDateOfTenant(), currency, recurringDepositAmount, runningBalance, false, transfer); + } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformService.java new file mode 100644 index 000000000..08a726c71 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformService.java @@ -0,0 +1,59 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.service; + +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.organisation.office.domain.Office; +import org.mifosplatform.organisation.staff.domain.Staff; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction; + +public interface DepositAccountWritePlatformService { + + CommandProcessingResult activate(Long savingsId, JsonCommand command, final DepositAccountType depositAccountType); + + CommandProcessingResult deposit(Long savingsId, JsonCommand command, final DepositAccountType depositAccountType); + + CommandProcessingResult withdrawal(Long savingsId, JsonCommand command, final DepositAccountType depositAccountType); + + CommandProcessingResult calculateInterest(Long savingsId, final DepositAccountType depositAccountType); + + CommandProcessingResult postInterest(Long savingsId, final DepositAccountType depositAccountType); + + CommandProcessingResult undoTransaction(Long savingsId, Long transactionId, boolean allowAccountTransferModification, final DepositAccountType depositAccountType); + + void postInterestForAccounts(); + + CommandProcessingResult adjustDepositTransaction(Long savingsId, Long transactionId, JsonCommand command, final DepositAccountType depositAccountType); + + CommandProcessingResult close(Long savingsId, JsonCommand command, final DepositAccountType depositAccountType); + + CommandProcessingResult prematureClose(Long savingsId, JsonCommand command, final DepositAccountType depositAccountType); + + SavingsAccountTransaction initiateSavingsTransfer(Long accountId, LocalDate transferDate, final DepositAccountType depositAccountType); + + SavingsAccountTransaction withdrawSavingsTransfer(Long accountId, LocalDate transferDate, final DepositAccountType depositAccountType); + + void rejectSavingsTransfer(Long accountId, final DepositAccountType depositAccountType); + + SavingsAccountTransaction acceptSavingsTransfer(Long accountId, LocalDate transferDate, Office acceptedInOffice, Staff staff, final DepositAccountType depositAccountType); + + CommandProcessingResult addSavingsAccountCharge(JsonCommand command, final DepositAccountType depositAccountType); + + CommandProcessingResult updateSavingsAccountCharge(JsonCommand command, final DepositAccountType depositAccountType); + + CommandProcessingResult deleteSavingsAccountCharge(Long savingsAccountId, Long savingsAccountChargeId, JsonCommand command, final DepositAccountType depositAccountType); + + CommandProcessingResult waiveCharge(Long savingsAccountId, Long savingsAccountChargeId, final DepositAccountType depositAccountType); + + CommandProcessingResult payCharge(Long savingsAccountId, Long savingsAccountChargeId, JsonCommand command, final DepositAccountType depositAccountType); + + void applyChargeDue(final Long savingsAccountChargeId, final Long accountId, final DepositAccountType depositAccountType); + + void updateMaturityDetails(final Long depositAccountId, final DepositAccountType depositAccountType); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 000000000..e824071fc --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,912 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.service; + +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.amountParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeIdParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.core.exception.PlatformServiceUnavailableException; +import org.mifosplatform.infrastructure.core.service.DateUtils; +import org.mifosplatform.infrastructure.jobs.annotation.CronTarget; +import org.mifosplatform.infrastructure.jobs.service.JobName; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.organisation.holiday.service.HolidayWritePlatformService; +import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; +import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper; +import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; +import org.mifosplatform.organisation.office.domain.Office; +import org.mifosplatform.organisation.staff.domain.Staff; +import org.mifosplatform.organisation.workingdays.service.WorkingDaysWritePlatformService; +import org.mifosplatform.portfolio.account.PortfolioAccountType; +import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService; +import org.mifosplatform.portfolio.charge.domain.Charge; +import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper; +import org.mifosplatform.portfolio.client.domain.Client; +import org.mifosplatform.portfolio.client.exception.ClientNotActiveException; +import org.mifosplatform.portfolio.group.domain.Group; +import org.mifosplatform.portfolio.group.exception.GroupNotActiveException; +import org.mifosplatform.portfolio.note.domain.Note; +import org.mifosplatform.portfolio.note.domain.NoteRepository; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; +import org.mifosplatform.portfolio.paymentdetail.service.PaymentDetailWritePlatformService; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.SavingsAccountTransactionType; +import org.mifosplatform.portfolio.savings.SavingsApiConstants; +import org.mifosplatform.portfolio.savings.data.DepositAccountTransactionDataValidator; +import org.mifosplatform.portfolio.savings.data.SavingsAccountChargeDataValidator; +import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionDTO; +import org.mifosplatform.portfolio.savings.domain.DepositAccountAssembler; +import org.mifosplatform.portfolio.savings.domain.DepositAccountDomainService; +import org.mifosplatform.portfolio.savings.domain.FixedDepositAccount; +import org.mifosplatform.portfolio.savings.domain.RecurringDepositAccount; +import org.mifosplatform.portfolio.savings.domain.SavingsAccount; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountCharge; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountChargeRepositoryWrapper; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepository; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountStatusType; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransactionRepository; +import org.mifosplatform.portfolio.savings.exception.DepositAccountTransactionNotAllowedException; +import org.mifosplatform.portfolio.savings.exception.SavingsAccountTransactionNotFoundException; +import org.mifosplatform.portfolio.savings.exception.TransactionUpdateNotAllowedException; +import org.mifosplatform.useradministration.domain.AppUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements DepositAccountWritePlatformService { + + private final PlatformSecurityContext context; + private final SavingsAccountRepository savingAccountRepository; + private final SavingsAccountTransactionRepository savingsAccountTransactionRepository; + private final DepositAccountAssembler depositAccountAssembler; + private final DepositAccountTransactionDataValidator depositAccountTransactionDataValidator; + private final SavingsAccountChargeDataValidator savingsAccountChargeDataValidator; + private final PaymentDetailWritePlatformService paymentDetailWritePlatformService; + private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper; + private final JournalEntryWritePlatformService journalEntryWritePlatformService; + private final DepositAccountDomainService depositAccountDomainService; + private final NoteRepository noteRepository; + private final AccountTransfersReadPlatformService accountTransfersReadPlatformService; + private final ChargeRepositoryWrapper chargeRepository; + private final SavingsAccountChargeRepositoryWrapper savingsAccountChargeRepository; + private final HolidayWritePlatformService holidayWritePlatformService; + private final WorkingDaysWritePlatformService workingDaysWritePlatformService; + + @Autowired + public DepositAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, + final SavingsAccountRepository savingAccountRepository, + final SavingsAccountTransactionRepository savingsAccountTransactionRepository, + final DepositAccountAssembler depositAccountAssembler, + final DepositAccountTransactionDataValidator depositAccountTransactionDataValidator, + final SavingsAccountChargeDataValidator savingsAccountChargeDataValidator, + final PaymentDetailWritePlatformService paymentDetailWritePlatformService, + final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper, + final JournalEntryWritePlatformService journalEntryWritePlatformService, + final DepositAccountDomainService depositAccountDomainService, final NoteRepository noteRepository, + final AccountTransfersReadPlatformService accountTransfersReadPlatformService, final ChargeRepositoryWrapper chargeRepository, + final SavingsAccountChargeRepositoryWrapper savingsAccountChargeRepository, + final HolidayWritePlatformService holidayWritePlatformService, + final WorkingDaysWritePlatformService workingDaysWritePlatformService) { + this.context = context; + this.savingAccountRepository = savingAccountRepository; + this.savingsAccountTransactionRepository = savingsAccountTransactionRepository; + this.depositAccountAssembler = depositAccountAssembler; + this.depositAccountTransactionDataValidator = depositAccountTransactionDataValidator; + this.savingsAccountChargeDataValidator = savingsAccountChargeDataValidator; + this.paymentDetailWritePlatformService = paymentDetailWritePlatformService; + this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper; + this.journalEntryWritePlatformService = journalEntryWritePlatformService; + this.depositAccountDomainService = depositAccountDomainService; + this.noteRepository = noteRepository; + this.accountTransfersReadPlatformService = accountTransfersReadPlatformService; + this.chargeRepository = chargeRepository; + this.savingsAccountChargeRepository = savingsAccountChargeRepository; + this.holidayWritePlatformService = holidayWritePlatformService; + this.workingDaysWritePlatformService = workingDaysWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult activate(final Long savingsId, final JsonCommand command, final DepositAccountType depositAccountType) { + + final AppUser user = this.context.authenticatedUser(); + + this.depositAccountTransactionDataValidator.validateActivation(command); + + final SavingsAccount account = this.depositAccountAssembler.assembleFrom(savingsId, depositAccountType); + checkClientOrGroupActive(account); + + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); + + final Map changes = account.activate(user, command, DateUtils.getLocalDateOfTenant()); + if (!changes.isEmpty()) { + + final MathContext mc = MathContext.DECIMAL64; + if (account.isBeforeLastPostingPeriod(account.getActivationLocalDate())) { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.postInterest(mc, today); + } else { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.calculateInterestUsing(mc, today); + } + + account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name()); + + this.savingAccountRepository.save(account); + } + + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + + return new CommandProcessingResultBuilder() // + .withEntityId(savingsId) // + .withOfficeId(account.officeId()) // + .withClientId(account.clientId()) // + .withGroupId(account.groupId()) // + .withSavingsId(savingsId) // + .with(changes) // + .build(); + } + + @Transactional + @Override + public CommandProcessingResult deposit(final Long savingsId, final JsonCommand command, final DepositAccountType depositAccountType) { + + this.context.authenticatedUser(); + + if(depositAccountType.isFixedDeposit()){ + throw new DepositAccountTransactionNotAllowedException(savingsId, "deposit", depositAccountType); + } + + this.depositAccountTransactionDataValidator.validate(command, depositAccountType); + + final SavingsAccount account = this.depositAccountAssembler.assembleFrom(savingsId, depositAccountType); + checkClientOrGroupActive(account); + + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); + + final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); + final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); + + final Map changes = new LinkedHashMap(); + final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); + + final SavingsAccountTransaction deposit = this.depositAccountDomainService.handleDeposit(account, fmt, transactionDate, + transactionAmount, paymentDetail); + + return new CommandProcessingResultBuilder() // + .withEntityId(deposit.getId()) // + .withOfficeId(account.officeId()) // + .withClientId(account.clientId()) // + .withGroupId(account.groupId()) // + .withSavingsId(savingsId) // + .with(changes) // + .build(); + } + + private Long saveTransactionToGenerateTransactionId(final SavingsAccountTransaction transaction) { + this.savingsAccountTransactionRepository.save(transaction); + return transaction.getId(); + } + + @Transactional + @Override + public CommandProcessingResult withdrawal(final Long savingsId, final JsonCommand command, final DepositAccountType depositAccountType) { + + this.depositAccountTransactionDataValidator.validate(command, depositAccountType); + + if(depositAccountType.isFixedDeposit()){ + throw new DepositAccountTransactionNotAllowedException(savingsId, "withdraw", depositAccountType); + } + + final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); + final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); + + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); + + final Map changes = new LinkedHashMap(); + final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); + + final SavingsAccount account = this.depositAccountAssembler.assembleFrom(savingsId, depositAccountType); + checkClientOrGroupActive(account); + + final SavingsAccountTransaction withdrawal = this.depositAccountDomainService.handleWithdrawal(account, fmt, transactionDate, + transactionAmount, paymentDetail, true); + + return new CommandProcessingResultBuilder() // + .withEntityId(withdrawal.getId()) // + .withOfficeId(account.officeId()) // + .withClientId(account.clientId()) // + .withGroupId(account.groupId()) // + .withSavingsId(savingsId) // + .with(changes)// + .build(); + } + + @Transactional + @Override + public CommandProcessingResult calculateInterest(final Long savingsId, final DepositAccountType depositAccountType) { + + final SavingsAccount account = this.depositAccountAssembler.assembleFrom(savingsId, depositAccountType); + checkClientOrGroupActive(account); + + final LocalDate today = DateUtils.getLocalDateOfTenant(); + final MathContext mc = new MathContext(15, RoundingMode.HALF_EVEN); + + account.calculateInterestUsing(mc, today); + + this.savingAccountRepository.save(account); + + return new CommandProcessingResultBuilder() // + .withEntityId(savingsId) // + .withOfficeId(account.officeId()) // + .withClientId(account.clientId()) // + .withGroupId(account.groupId()) // + .withSavingsId(savingsId) // + .build(); + } + + @Transactional + @Override + public CommandProcessingResult postInterest(final Long savingsId, final DepositAccountType depositAccountType) { + + final SavingsAccount account = this.depositAccountAssembler.assembleFrom(savingsId, depositAccountType); + checkClientOrGroupActive(account); + postInterest(account); + return new CommandProcessingResultBuilder() // + .withEntityId(savingsId) // + .withOfficeId(account.officeId()) // + .withClientId(account.clientId()) // + .withGroupId(account.groupId()) // + .withSavingsId(savingsId) // + .build(); + } + + @Transactional + private void postInterest(final SavingsAccount account) { + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); + final LocalDate today = DateUtils.getLocalDateOfTenant(); + final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN); + + account.postInterest(mc, today); + this.savingAccountRepository.save(account); + + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + } + + @CronTarget(jobName = JobName.POST_INTEREST_FOR_SAVINGS) + @Override + public void postInterestForAccounts() { + final List savingsAccounts = this.savingAccountRepository.findSavingAccountByStatus(SavingsAccountStatusType.ACTIVE + .getValue()); + for (final SavingsAccount savingsAccount : savingsAccounts) { + this.depositAccountAssembler.assignSavingAccountHelpers(savingsAccount); + postInterest(savingsAccount); + } + } + + @Override + public CommandProcessingResult undoTransaction(final Long savingsId, final Long transactionId, + final boolean allowAccountTransferModification, final DepositAccountType depositAccountType) { + + if(depositAccountType.isFixedDeposit()){ + throw new DepositAccountTransactionNotAllowedException(savingsId, "undo", depositAccountType); + } + + final SavingsAccount account = this.depositAccountAssembler.assembleFrom(savingsId, depositAccountType); + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); + + final SavingsAccountTransaction savingsAccountTransaction = this.savingsAccountTransactionRepository + .findOneByIdAndSavingsAccountId(transactionId, savingsId); + if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); } + + if (!allowAccountTransferModification + && this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException( + "error.msg.saving.account.transfer.transaction.update.not.allowed", "Savings account transaction:" + transactionId + + " update not allowed as it involves in account transfer", transactionId); } + + final LocalDate today = DateUtils.getLocalDateOfTenant(); + final MathContext mc = new MathContext(15, RoundingMode.HALF_EVEN); + + if (account.isNotActive()) { + throwValidationForActiveStatus(SavingsApiConstants.undoTransactionAction); + } + account.undoTransaction(transactionId); + + checkClientOrGroupActive(account); + if (savingsAccountTransaction.isPostInterestCalculationRequired() + && account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) { + account.postInterest(mc, today); + } else { + account.calculateInterestUsing(mc, today); + } + account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction); + account.activateAccountBasedOnBalance(); + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + + return new CommandProcessingResultBuilder() // + .withEntityId(savingsId) // + .withOfficeId(account.officeId()) // + .withClientId(account.clientId()) // + .withGroupId(account.groupId()) // + .withSavingsId(savingsId) // + .build(); + } + + @Override + public CommandProcessingResult adjustDepositTransaction(final Long savingsId, final Long transactionId, final JsonCommand command, + final DepositAccountType depositAccountType) { + + if(depositAccountType.isFixedDeposit()){ + throw new DepositAccountTransactionNotAllowedException(savingsId, "modify", depositAccountType); + } + this.depositAccountTransactionDataValidator.validate(command, depositAccountType); + + final SavingsAccountTransaction savingsAccountTransaction = this.savingsAccountTransactionRepository + .findOneByIdAndSavingsAccountId(transactionId, savingsId); + if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); } + + if (!(savingsAccountTransaction.isDeposit() || savingsAccountTransaction.isWithdrawal()) || savingsAccountTransaction.isReversed()) { throw new TransactionUpdateNotAllowedException( + savingsId, transactionId); } + + if (this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException( + "error.msg.saving.account.transfer.transaction.update.not.allowed", "Deposit account transaction:" + transactionId + + " update not allowed as it involves in account transfer", transactionId); } + + + final LocalDate today = DateUtils.getLocalDateOfTenant(); + + final SavingsAccount account = this.depositAccountAssembler.assembleFrom(savingsId, depositAccountType); + if (account.isNotActive()) { + throwValidationForActiveStatus(SavingsApiConstants.adjustTransactionAction); + } + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); + + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); + final LocalDate transactionDate = command.localDateValueOfParameterNamed(SavingsApiConstants.transactionDateParamName); + final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(SavingsApiConstants.transactionAmountParamName); + final Map changes = new LinkedHashMap(); + final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); + + final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN); + account.undoTransaction(transactionId); + + SavingsAccountTransaction transaction = null; + if (savingsAccountTransaction.isDeposit()) { + final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount, + paymentDetail, savingsAccountTransaction.createdDate()); + transaction = account.deposit(transactionDTO); + } else { + final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount, + paymentDetail, savingsAccountTransaction.createdDate()); + transaction = account.withdraw(transactionDTO, true); + } + final Long newtransactionId = saveTransactionToGenerateTransactionId(transaction); + + if (account.isBeforeLastPostingPeriod(transactionDate) + || account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) { + account.postInterest(mc, today); + } else { + account.calculateInterestUsing(mc, today); + } + account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.adjustTransactionAction); + account.activateAccountBasedOnBalance(); + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + return new CommandProcessingResultBuilder() // + .withEntityId(newtransactionId) // + .withOfficeId(account.officeId()) // + .withClientId(account.clientId()) // + .withGroupId(account.groupId()) // + .withSavingsId(savingsId) // + .with(changes)// + .build(); + } + + /** + * + */ + private void throwValidationForActiveStatus(final String actionName) { + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(SAVINGS_ACCOUNT_RESOURCE_NAME + actionName); + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("account.is.not.active"); + throw new PlatformApiDataValidationException(dataValidationErrors); + } + + private void checkClientOrGroupActive(final SavingsAccount account) { + final Client client = account.getClient(); + if (client != null) { + if (client.isNotActive()) { throw new ClientNotActiveException(client.getId()); } + } + final Group group = account.group(); + if (group != null) { + if (group.isNotActive()) { throw new GroupNotActiveException(group.getId()); } + } + } + + @Override + public CommandProcessingResult close(final Long savingsId, final JsonCommand command, final DepositAccountType depositAccountType) { + final AppUser user = this.context.authenticatedUser(); + + this.depositAccountTransactionDataValidator.validateClosing(command, depositAccountType, false); + + final Map changes = new LinkedHashMap(); + final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); + + final SavingsAccount account = this.depositAccountAssembler.assembleFrom(savingsId, depositAccountType); + checkClientOrGroupActive(account); + + final SavingsAccountTransaction withdrawal = this.depositAccountDomainService.handleAccountClosure(account, paymentDetail, user, + command, DateUtils.getLocalDateOfTenant(), changes); + + final String noteText = command.stringValueOfParameterNamed("note"); + if (StringUtils.isNotBlank(noteText)) { + final Note note = Note.savingNote(account, noteText); + changes.put("note", noteText); + this.noteRepository.save(note); + } + + return new CommandProcessingResultBuilder() // + .withEntityId(withdrawal.getId()) // + .withOfficeId(account.officeId()) // + .withClientId(account.clientId()) // + .withGroupId(account.groupId()) // + .withSavingsId(savingsId) // + .with(changes)// + .build(); + + } + + @Override + public CommandProcessingResult prematureClose(final Long savingsId, final JsonCommand command, + final DepositAccountType depositAccountType) { + final AppUser user = this.context.authenticatedUser(); + + this.depositAccountTransactionDataValidator.validateClosing(command, depositAccountType, true); + + final Map changes = new LinkedHashMap(); + final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); + + final SavingsAccount account = this.depositAccountAssembler.assembleFrom(savingsId, depositAccountType); + checkClientOrGroupActive(account); + + final SavingsAccountTransaction withdrawal = this.depositAccountDomainService.handleAccountPreMatureClosure(account, paymentDetail, + user, command, DateUtils.getLocalDateOfTenant(), changes); + + final String noteText = command.stringValueOfParameterNamed("note"); + if (StringUtils.isNotBlank(noteText)) { + final Note note = Note.savingNote(account, noteText); + changes.put("note", noteText); + this.noteRepository.save(note); + } + + return new CommandProcessingResultBuilder() // + .withEntityId(withdrawal.getId()) // + .withOfficeId(account.officeId()) // + .withClientId(account.clientId()) // + .withGroupId(account.groupId()) // + .withSavingsId(savingsId) // + .with(changes)// + .build(); + + } + + @Override + public SavingsAccountTransaction initiateSavingsTransfer(final Long accountId, final LocalDate transferDate, + final DepositAccountType depositAccountType) { + final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(accountId, depositAccountType); + + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(savingsAccount, existingTransactionIds, existingReversedTransactionIds); + + final SavingsAccountTransaction newTransferTransaction = SavingsAccountTransaction.initiateTransfer(savingsAccount, + savingsAccount.office(), transferDate); + savingsAccount.getTransactions().add(newTransferTransaction); + savingsAccount.setStatus(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue()); + final MathContext mc = MathContext.DECIMAL64; + savingsAccount.calculateInterestUsing(mc, transferDate); + + this.savingsAccountTransactionRepository.save(newTransferTransaction); + this.savingAccountRepository.save(savingsAccount); + + postJournalEntries(savingsAccount, existingTransactionIds, existingReversedTransactionIds); + + return newTransferTransaction; + } + + @Override + public SavingsAccountTransaction withdrawSavingsTransfer(final Long accountId, final LocalDate transferDate, + final DepositAccountType depositAccountType) { + final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(accountId, depositAccountType); + + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(savingsAccount, existingTransactionIds, existingReversedTransactionIds); + + final SavingsAccountTransaction withdrawtransferTransaction = SavingsAccountTransaction.withdrawTransfer(savingsAccount, + savingsAccount.office(), transferDate); + savingsAccount.getTransactions().add(withdrawtransferTransaction); + savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue()); + final MathContext mc = MathContext.DECIMAL64; + savingsAccount.calculateInterestUsing(mc, transferDate); + + this.savingsAccountTransactionRepository.save(withdrawtransferTransaction); + this.savingAccountRepository.save(savingsAccount); + + postJournalEntries(savingsAccount, existingTransactionIds, existingReversedTransactionIds); + + return withdrawtransferTransaction; + } + + @Override + public void rejectSavingsTransfer(final Long accountId, final DepositAccountType depositAccountType) { + final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(accountId, depositAccountType); + savingsAccount.setStatus(SavingsAccountStatusType.TRANSFER_ON_HOLD.getValue()); + this.savingAccountRepository.save(savingsAccount); + } + + @Override + public SavingsAccountTransaction acceptSavingsTransfer(final Long accountId, final LocalDate transferDate, + final Office acceptedInOffice, final Staff fieldOfficer, final DepositAccountType depositAccountType) { + final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(accountId, depositAccountType); + + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(savingsAccount, existingTransactionIds, existingReversedTransactionIds); + + final SavingsAccountTransaction acceptTransferTransaction = SavingsAccountTransaction.approveTransfer(savingsAccount, + acceptedInOffice, transferDate); + savingsAccount.getTransactions().add(acceptTransferTransaction); + savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue()); + if (fieldOfficer != null) { + savingsAccount.update(fieldOfficer); + } + + final MathContext mc = MathContext.DECIMAL64; + savingsAccount.calculateInterestUsing(mc, transferDate); + + this.savingsAccountTransactionRepository.save(acceptTransferTransaction); + this.savingAccountRepository.save(savingsAccount); + + postJournalEntries(savingsAccount, existingTransactionIds, existingReversedTransactionIds); + + return acceptTransferTransaction; + } + + @Transactional + @Override + public CommandProcessingResult addSavingsAccountCharge(final JsonCommand command, final DepositAccountType depositAccountType) { + + this.context.authenticatedUser(); + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(SAVINGS_ACCOUNT_RESOURCE_NAME); + + final Long savingsAccountId = command.getSavingsId(); + this.savingsAccountChargeDataValidator.validateAdd(command.json()); + + final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(savingsAccountId, depositAccountType); + checkClientOrGroupActive(savingsAccount); + + final Locale locale = command.extractLocale(); + final String format = command.dateFormat(); + final DateTimeFormatter fmt = StringUtils.isNotBlank(format) ? DateTimeFormat.forPattern(format).withLocale(locale) + : DateTimeFormat.forPattern("dd MM yyyy"); + + final Long chargeDefinitionId = command.longValueOfParameterNamed(chargeIdParamName); + final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeDefinitionId); + + final SavingsAccountCharge savingsAccountCharge = SavingsAccountCharge.createNewFromJson(savingsAccount, chargeDefinition, command); + + if (savingsAccountCharge.getDueLocalDate() != null) { + // transaction date should not be on a holiday or non working day + if (!this.holidayWritePlatformService.isTransactionAllowedOnHoliday() + && this.holidayWritePlatformService.isHoliday(savingsAccount.officeId(), savingsAccountCharge.getDueLocalDate())) { + baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().toString(fmt)) + .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.on.holiday"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + if (!this.workingDaysWritePlatformService.isTransactionAllowedOnNonWorkingDay() + && !this.workingDaysWritePlatformService.isWorkingDay(savingsAccountCharge.getDueLocalDate())) { + baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().toString(fmt)) + .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.a.nonworking.day"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + } + + savingsAccount.addCharge(fmt, savingsAccountCharge, chargeDefinition); + + this.savingAccountRepository.saveAndFlush(savingsAccount); + + return new CommandProcessingResultBuilder() // + .withEntityId(savingsAccountCharge.getId()) // + .withOfficeId(savingsAccount.officeId()) // + .withClientId(savingsAccount.clientId()) // + .withGroupId(savingsAccount.groupId()) // + .withSavingsId(savingsAccountId) // + .build(); + } + + @Transactional + @Override + public CommandProcessingResult updateSavingsAccountCharge(final JsonCommand command, final DepositAccountType depositAccountType) { + + this.context.authenticatedUser(); + this.savingsAccountChargeDataValidator.validateUpdate(command.json()); + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(SAVINGS_ACCOUNT_RESOURCE_NAME); + + final Long savingsAccountId = command.getSavingsId(); + // SavingsAccount Charge entity + final Long savingsChargeId = command.entityId(); + + final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(savingsAccountId, depositAccountType); + checkClientOrGroupActive(savingsAccount); + + final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(savingsChargeId, + savingsAccountId); + + final Map changes = savingsAccountCharge.update(command); + + if (savingsAccountCharge.getDueLocalDate() != null) { + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); + + // transaction date should not be on a holiday or non working day + if (!this.holidayWritePlatformService.isTransactionAllowedOnHoliday() + && this.holidayWritePlatformService.isHoliday(savingsAccount.officeId(), savingsAccountCharge.getDueLocalDate())) { + baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().toString(fmt)) + .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.on.holiday"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + if (!this.workingDaysWritePlatformService.isTransactionAllowedOnNonWorkingDay() + && !this.workingDaysWritePlatformService.isWorkingDay(savingsAccountCharge.getDueLocalDate())) { + baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().toString(fmt)) + .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.a.nonworking.day"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + } + + this.savingsAccountChargeRepository.saveAndFlush(savingsAccountCharge); + + return new CommandProcessingResultBuilder() // + .withEntityId(savingsAccountCharge.getId()) // + .withOfficeId(savingsAccountCharge.savingsAccount().officeId()) // + .withClientId(savingsAccountCharge.savingsAccount().clientId()) // + .withGroupId(savingsAccountCharge.savingsAccount().groupId()) // + .withSavingsId(savingsAccountCharge.savingsAccount().getId()) // + .with(changes) // + .build(); + } + + @Transactional + @Override + public CommandProcessingResult waiveCharge(final Long savingsAccountId, final Long savingsAccountChargeId, + @SuppressWarnings("unused") final DepositAccountType depositAccountType) { + + this.context.authenticatedUser(); + + final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection( + savingsAccountChargeId, savingsAccountId); + + // Get Savings account from savings charge + final SavingsAccount account = savingsAccountCharge.savingsAccount(); + this.depositAccountAssembler.assignSavingAccountHelpers(account); + + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); + + account.waiveCharge(savingsAccountChargeId); + + final MathContext mc = MathContext.DECIMAL64; + if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate())) { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.postInterest(mc, today); + } else { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.calculateInterestUsing(mc, today); + } + + account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.waiveChargeTransactionAction); + + this.savingAccountRepository.saveAndFlush(account); + + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + + return new CommandProcessingResultBuilder() // + .withEntityId(savingsAccountChargeId) // + .withOfficeId(account.officeId()) // + .withClientId(account.clientId()) // + .withGroupId(account.groupId()) // + .withSavingsId(savingsAccountId) // + .build(); + } + + @Transactional + @Override + public CommandProcessingResult deleteSavingsAccountCharge(final Long savingsAccountId, final Long savingsAccountChargeId, + @SuppressWarnings("unused") final JsonCommand command, final DepositAccountType depositAccountType) { + this.context.authenticatedUser(); + + final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(savingsAccountId, depositAccountType); + checkClientOrGroupActive(savingsAccount); + final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection( + savingsAccountChargeId, savingsAccountId); + + savingsAccount.removeCharge(savingsAccountCharge); + this.savingAccountRepository.saveAndFlush(savingsAccount); + + return new CommandProcessingResultBuilder() // + .withEntityId(savingsAccountChargeId) // + .withOfficeId(savingsAccount.officeId()) // + .withClientId(savingsAccount.clientId()) // + .withGroupId(savingsAccount.groupId()) // + .withSavingsId(savingsAccountId) // + .build(); + } + + @Override + public CommandProcessingResult payCharge(final Long savingsAccountId, final Long savingsAccountChargeId, final JsonCommand command, + @SuppressWarnings("unused") final DepositAccountType depositAccountType) { + + this.context.authenticatedUser(); + this.savingsAccountChargeDataValidator.validatePayCharge(command.json()); + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); + final BigDecimal amountPaid = command.bigDecimalValueOfParameterNamed(amountParamName); + final LocalDate transactionDate = command.localDateValueOfParameterNamed(dueAsOfDateParamName); + + final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection( + savingsAccountChargeId, savingsAccountId); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(SAVINGS_ACCOUNT_RESOURCE_NAME); + + // transaction date should not be on a holiday or non working day + if (!this.holidayWritePlatformService.isTransactionAllowedOnHoliday() + && this.holidayWritePlatformService.isHoliday(savingsAccountCharge.savingsAccount().officeId(), transactionDate)) { + baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate.toString(fmt)) + .failWithCodeNoParameterAddedToErrorCode("transaction.not.allowed.transaction.date.is.on.holiday"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + if (!this.workingDaysWritePlatformService.isTransactionAllowedOnNonWorkingDay() + && !this.workingDaysWritePlatformService.isWorkingDay(transactionDate)) { + baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate.toString(fmt)) + .failWithCodeNoParameterAddedToErrorCode("transaction.not.allowed.transaction.date.is.a.nonworking.day"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + this.payCharge(savingsAccountCharge, transactionDate, amountPaid, fmt); + return new CommandProcessingResultBuilder() // + .withEntityId(savingsAccountCharge.getId()) // + .withOfficeId(savingsAccountCharge.savingsAccount().officeId()) // + .withClientId(savingsAccountCharge.savingsAccount().clientId()) // + .withGroupId(savingsAccountCharge.savingsAccount().groupId()) // + .withSavingsId(savingsAccountCharge.savingsAccount().getId()) // + .build(); + + } + + @Transactional + @Override + public void applyChargeDue(final Long savingsAccountChargeId, final Long accountId, + @SuppressWarnings("unused") final DepositAccountType depositAccountType) { + // always use current date as transaction date for batch job + final LocalDate transactionDate = DateUtils.getLocalDateOfTenant(); + final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection( + savingsAccountChargeId, accountId); + + final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd MM yyyy"); + + while (transactionDate.isAfter(savingsAccountCharge.getDueLocalDate())) { + payCharge(savingsAccountCharge, transactionDate, savingsAccountCharge.amoutOutstanding(), fmt); + } + } + + @Transactional + private void payCharge(final SavingsAccountCharge savingsAccountCharge, final LocalDate transactionDate, final BigDecimal amountPaid, + final DateTimeFormatter formatter) { + // Get Savings account from savings charge + final SavingsAccount account = savingsAccountCharge.savingsAccount(); + this.depositAccountAssembler.assignSavingAccountHelpers(account); + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); + account.payCharge(savingsAccountCharge, amountPaid, transactionDate, formatter); + + final MathContext mc = MathContext.DECIMAL64; + if (account.isBeforeLastPostingPeriod(transactionDate)) { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.postInterest(mc, today); + } else { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.calculateInterestUsing(mc, today); + } + + account.validateAccountBalanceDoesNotBecomeNegative("." + SavingsAccountTransactionType.PAY_CHARGE.getCode()); + + this.savingAccountRepository.save(account); + + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + } + + @Transactional + @Override + public void updateMaturityDetails(Long depositAccountId, DepositAccountType depositAccountType) { + final SavingsAccount account = this.depositAccountAssembler.assembleFrom(depositAccountId, depositAccountType); + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); + + if (depositAccountType.isFixedDeposit()) { + ((FixedDepositAccount) account).updateMaturityStatus(); + } else if (depositAccountType.isRecurringDeposit()) { + ((RecurringDepositAccount) account).updateMaturityStatus(); + } + + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + } + + private void updateExistingTransactionsDetails(SavingsAccount account, Set existingTransactionIds, + Set existingReversedTransactionIds) { + existingTransactionIds.addAll(account.findExistingTransactionIds()); + existingReversedTransactionIds.addAll(account.findExistingReversedTransactionIds()); + } + + private void postJournalEntries(final SavingsAccount savingsAccount, final Set existingTransactionIds, + final Set existingReversedTransactionIds) { + + final MonetaryCurrency currency = savingsAccount.getCurrency(); + final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency); + + final Map accountingBridgeData = savingsAccount.deriveAccountingBridgeData(applicationCurrency.toData(), + existingTransactionIds, existingReversedTransactionIds); + this.journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositApplicationProcessWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositApplicationProcessWritePlatformService.java new file mode 100644 index 000000000..52704935e --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositApplicationProcessWritePlatformService.java @@ -0,0 +1,27 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.service; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.DepositAccountType; + +public interface DepositApplicationProcessWritePlatformService { + + CommandProcessingResult submitApplication(JsonCommand command, DepositAccountType depositAccountType); + + CommandProcessingResult modifyApplication(Long accountId, JsonCommand command, DepositAccountType depositAccountType); + + CommandProcessingResult deleteApplication(Long accountId, DepositAccountType depositAccountType); + + CommandProcessingResult approveApplication(Long accountId, JsonCommand command, DepositAccountType depositAccountType); + + CommandProcessingResult undoApplicationApproval(Long accountId, JsonCommand command, DepositAccountType depositAccountType); + + CommandProcessingResult rejectApplication(Long accountId, JsonCommand command, DepositAccountType depositAccountType); + + CommandProcessingResult applicantWithdrawsFromApplication(Long accountId, JsonCommand command, DepositAccountType depositAccountType); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 000000000..ec520aa4a --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,464 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.service; + +import java.math.MathContext; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; +import org.mifosplatform.infrastructure.core.service.DateUtils; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.organisation.staff.domain.Staff; +import org.mifosplatform.organisation.staff.domain.StaffRepositoryWrapper; +import org.mifosplatform.portfolio.client.domain.AccountNumberGenerator; +import org.mifosplatform.portfolio.client.domain.AccountNumberGeneratorFactory; +import org.mifosplatform.portfolio.client.domain.Client; +import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; +import org.mifosplatform.portfolio.client.exception.ClientNotActiveException; +import org.mifosplatform.portfolio.group.domain.Group; +import org.mifosplatform.portfolio.group.domain.GroupRepository; +import org.mifosplatform.portfolio.group.exception.CenterNotActiveException; +import org.mifosplatform.portfolio.group.exception.GroupNotActiveException; +import org.mifosplatform.portfolio.group.exception.GroupNotFoundException; +import org.mifosplatform.portfolio.note.domain.Note; +import org.mifosplatform.portfolio.note.domain.NoteRepository; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.DepositsApiConstants; +import org.mifosplatform.portfolio.savings.SavingsApiConstants; +import org.mifosplatform.portfolio.savings.data.DepositAccountDataValidator; +import org.mifosplatform.portfolio.savings.domain.DepositAccountAssembler; +import org.mifosplatform.portfolio.savings.domain.FixedDepositAccount; +import org.mifosplatform.portfolio.savings.domain.FixedDepositAccountRepository; +import org.mifosplatform.portfolio.savings.domain.RecurringDepositAccount; +import org.mifosplatform.portfolio.savings.domain.RecurringDepositAccountRepository; +import org.mifosplatform.portfolio.savings.domain.SavingsAccount; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountCharge; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountChargeAssembler; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepositoryWrapper; +import org.mifosplatform.portfolio.savings.domain.SavingsProduct; +import org.mifosplatform.portfolio.savings.domain.SavingsProductRepository; +import org.mifosplatform.portfolio.savings.exception.SavingsProductNotFoundException; +import org.mifosplatform.useradministration.domain.AppUser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl implements DepositApplicationProcessWritePlatformService { + + private final static Logger logger = LoggerFactory.getLogger(DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl.class); + + private final PlatformSecurityContext context; + private final SavingsAccountRepositoryWrapper savingAccountRepository; + private final FixedDepositAccountRepository fixedDepositAccountRepository; + private final RecurringDepositAccountRepository recurringDepositAccountRepository; + private final DepositAccountAssembler depositAccountAssembler; + private final DepositAccountDataValidator depositAccountDataValidator; + private final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory; + private final ClientRepositoryWrapper clientRepository; + private final GroupRepository groupRepository; + private final SavingsProductRepository savingsProductRepository; + private final NoteRepository noteRepository; + private final StaffRepositoryWrapper staffRepository; + private final SavingsAccountApplicationTransitionApiJsonValidator savingsAccountApplicationTransitionApiJsonValidator; + private final SavingsAccountChargeAssembler savingsAccountChargeAssembler; + + @Autowired + public DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, + final SavingsAccountRepositoryWrapper savingAccountRepository, final DepositAccountAssembler depositAccountAssembler, + final DepositAccountDataValidator depositAccountDataValidator, + final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory, final ClientRepositoryWrapper clientRepository, + final GroupRepository groupRepository, final SavingsProductRepository savingsProductRepository, + final NoteRepository noteRepository, final StaffRepositoryWrapper staffRepository, + final SavingsAccountApplicationTransitionApiJsonValidator savingsAccountApplicationTransitionApiJsonValidator, + final SavingsAccountChargeAssembler savingsAccountChargeAssembler, + final FixedDepositAccountRepository fixedDepositAccountRepository, + final RecurringDepositAccountRepository recurringDepositAccountRepository) { + this.context = context; + this.savingAccountRepository = savingAccountRepository; + this.depositAccountAssembler = depositAccountAssembler; + this.accountIdentifierGeneratorFactory = accountIdentifierGeneratorFactory; + this.depositAccountDataValidator = depositAccountDataValidator; + this.clientRepository = clientRepository; + this.groupRepository = groupRepository; + this.savingsProductRepository = savingsProductRepository; + this.noteRepository = noteRepository; + this.staffRepository = staffRepository; + this.savingsAccountApplicationTransitionApiJsonValidator = savingsAccountApplicationTransitionApiJsonValidator; + this.savingsAccountChargeAssembler = savingsAccountChargeAssembler; + this.fixedDepositAccountRepository = fixedDepositAccountRepository; + this.recurringDepositAccountRepository = recurringDepositAccountRepository; + } + + /* + * Guaranteed to throw an exception no matter what the data integrity issue + * is. + */ + private void handleDataIntegrityIssues(final JsonCommand command, final DataAccessException dve) { + + final StringBuilder errorCodeBuilder = new StringBuilder("error.msg.").append(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME); + + final Throwable realCause = dve.getMostSpecificCause(); + if (realCause.getMessage().contains("sa_account_no_UNIQUE")) { + final String accountNo = command.stringValueOfParameterNamed("accountNo"); + errorCodeBuilder.append(".duplicate.accountNo"); + throw new PlatformDataIntegrityException(errorCodeBuilder.toString(), "Savings account with accountNo " + accountNo + + " already exists", "accountNo", accountNo); + + } else if (realCause.getMessage().contains("sa_external_id_UNIQUE")) { + + final String externalId = command.stringValueOfParameterNamed("externalId"); + errorCodeBuilder.append(".duplicate.externalId"); + throw new PlatformDataIntegrityException(errorCodeBuilder.toString(), "Savings account with externalId " + externalId + + " already exists", "externalId", externalId); + } + + errorCodeBuilder.append(".unknown.data.integrity.issue"); + logger.error(dve.getMessage(), dve); + throw new PlatformDataIntegrityException(errorCodeBuilder.toString(), "Unknown data integrity issue with savings account."); + } + + @Transactional + @Override + public CommandProcessingResult submitApplication(final JsonCommand command, final DepositAccountType depositAccountType) { + try { + this.depositAccountDataValidator.validateForSubmit(command.json(), depositAccountType); + final AppUser submittedBy = this.context.authenticatedUser(); + + final SavingsAccount account = this.depositAccountAssembler.assembleFrom(command, submittedBy, depositAccountType); + + final MathContext mc = MathContext.DECIMAL64; + + if (depositAccountType.isFixedDeposit()) { + ((FixedDepositAccount) account).updateMaturityDateAndAmountBeforeAccountActivation(mc); + this.fixedDepositAccountRepository.saveAndFlush((FixedDepositAccount) account); + } else if (depositAccountType.isRecurringDeposit()) { + final LocalDate transactionStartDate = account.accountSubmittedOrActivationDate(); + ((RecurringDepositAccount) account).updateMaturityDateAndAmount(mc, transactionStartDate); + this.recurringDepositAccountRepository.save((RecurringDepositAccount) account); + } + + if (account.isAccountNumberRequiresAutoGeneration()) { + final AccountNumberGenerator accountNoGenerator = this.accountIdentifierGeneratorFactory + .determineSavingsAccountNoGenerator(account.getId()); + account.updateAccountNo(accountNoGenerator.generate()); + + this.savingAccountRepository.save(account); + } + + final Long savingsId = account.getId(); + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withEntityId(savingsId) // + .withOfficeId(account.officeId()) // + .withClientId(account.clientId()) // + .withGroupId(account.groupId()) // + .withSavingsId(savingsId) // + .build(); + } catch (final DataAccessException dve) { + handleDataIntegrityIssues(command, dve); + return CommandProcessingResult.empty(); + } + } + + @Transactional + @Override + public CommandProcessingResult modifyApplication(final Long accountId, final JsonCommand command, + final DepositAccountType depositAccountType) { + try { + this.depositAccountDataValidator.validateForUpdate(command.json(), depositAccountType); + + final Map changes = new LinkedHashMap(20); + + final SavingsAccount account = this.depositAccountAssembler.assembleFrom(accountId, depositAccountType); + checkClientOrGroupActive(account); + account.modifyApplication(command, changes); + account.validateNewApplicationState(DateUtils.getLocalDateOfTenant(), depositAccountType.resourceName()); + + if (!changes.isEmpty()) { + + if (changes.containsKey(SavingsApiConstants.clientIdParamName)) { + final Long clientId = command.longValueOfParameterNamed(SavingsApiConstants.clientIdParamName); + if (clientId != null) { + final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId); + if (client.isNotActive()) { throw new ClientNotActiveException(clientId); } + account.update(client); + } else { + final Client client = null; + account.update(client); + } + } + + if (changes.containsKey(SavingsApiConstants.groupIdParamName)) { + final Long groupId = command.longValueOfParameterNamed(SavingsApiConstants.groupIdParamName); + if (groupId != null) { + final Group group = this.groupRepository.findOne(groupId); + if (group == null) { throw new GroupNotFoundException(groupId); } + if (group.isNotActive()) { + if (group.isCenter()) { throw new CenterNotActiveException(groupId); } + throw new GroupNotActiveException(groupId); + } + account.update(group); + } else { + final Group group = null; + account.update(group); + } + } + + if (changes.containsKey(SavingsApiConstants.productIdParamName)) { + final Long productId = command.longValueOfParameterNamed(SavingsApiConstants.productIdParamName); + final SavingsProduct product = this.savingsProductRepository.findOne(productId); + if (product == null) { throw new SavingsProductNotFoundException(productId); } + + account.update(product); + } + + if (changes.containsKey(SavingsApiConstants.fieldOfficerIdParamName)) { + final Long fieldOfficerId = command.longValueOfParameterNamed(SavingsApiConstants.fieldOfficerIdParamName); + Staff fieldOfficer = null; + if (fieldOfficerId != null) { + fieldOfficer = this.staffRepository.findOneWithNotFoundDetection(fieldOfficerId); + } else { + changes.put(SavingsApiConstants.fieldOfficerIdParamName, ""); + } + account.update(fieldOfficer); + } + + if (changes.containsKey("charges")) { + final Set charges = this.savingsAccountChargeAssembler.fromParsedJson(command.parsedJson(), + account.getCurrency().getCode()); + final boolean updated = account.update(charges); + if (!updated) { + changes.remove("charges"); + } + } + final MathContext mc = MathContext.DECIMAL64; + if (depositAccountType.isFixedDeposit()) { + ((FixedDepositAccount) account).updateMaturityDateAndAmountBeforeAccountActivation(mc); + } else if (depositAccountType.isRecurringDeposit()) { + final LocalDate transactionStartDate = account.accountSubmittedOrActivationDate(); + ((RecurringDepositAccount) account).updateMaturityDateAndAmount(mc, transactionStartDate); + } + + this.savingAccountRepository.saveAndFlush(account); + } + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withEntityId(accountId) // + .withOfficeId(account.officeId()) // + .withClientId(account.clientId()) // + .withGroupId(account.groupId()) // + .withSavingsId(accountId) // + .with(changes) // + .build(); + } catch (final DataAccessException dve) { + handleDataIntegrityIssues(command, dve); + return new CommandProcessingResult(Long.valueOf(-1)); + } + } + + @Transactional + @Override + public CommandProcessingResult deleteApplication(final Long savingsId, final DepositAccountType depositAccountType) { + + final SavingsAccount account = this.depositAccountAssembler.assembleFrom(savingsId, depositAccountType); + checkClientOrGroupActive(account); + + if (account.isNotSubmittedAndPendingApproval()) { + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME + DepositsApiConstants.deleteApplicationAction); + + baseDataValidator.reset().parameter(DepositsApiConstants.activatedOnDateParamName) + .failWithCodeNoParameterAddedToErrorCode("not.in.submittedandpendingapproval.state"); + + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + final List relatedNotes = this.noteRepository.findBySavingsAccountId(savingsId); + this.noteRepository.deleteInBatch(relatedNotes); + + this.savingAccountRepository.delete(account); + + return new CommandProcessingResultBuilder() // + .withEntityId(savingsId) // + .withOfficeId(account.officeId()) // + .withClientId(account.clientId()) // + .withGroupId(account.groupId()) // + .withSavingsId(savingsId) // + .build(); + } + + @Transactional + @Override + public CommandProcessingResult approveApplication(final Long savingsId, final JsonCommand command, + final DepositAccountType depositAccountType) { + + final AppUser currentUser = this.context.authenticatedUser(); + + this.savingsAccountApplicationTransitionApiJsonValidator.validateApproval(command.json()); + + final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(savingsId, depositAccountType); + checkClientOrGroupActive(savingsAccount); + + final Map changes = savingsAccount.approveApplication(currentUser, command, DateUtils.getLocalDateOfTenant()); + if (!changes.isEmpty()) { + this.savingAccountRepository.save(savingsAccount); + + final String noteText = command.stringValueOfParameterNamed("note"); + if (StringUtils.isNotBlank(noteText)) { + final Note note = Note.savingNote(savingsAccount, noteText); + changes.put("note", noteText); + this.noteRepository.save(note); + } + } + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withEntityId(savingsId) // + .withOfficeId(savingsAccount.officeId()) // + .withClientId(savingsAccount.clientId()) // + .withGroupId(savingsAccount.groupId()) // + .withSavingsId(savingsId) // + .with(changes) // + .build(); + } + + @Transactional + @Override + public CommandProcessingResult undoApplicationApproval(final Long savingsId, final JsonCommand command, + final DepositAccountType depositAccountType) { + + this.context.authenticatedUser(); + + this.savingsAccountApplicationTransitionApiJsonValidator.validateForUndo(command.json()); + + final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(savingsId, depositAccountType); + checkClientOrGroupActive(savingsAccount); + + final Map changes = savingsAccount.undoApplicationApproval(); + if (!changes.isEmpty()) { + this.savingAccountRepository.save(savingsAccount); + + final String noteText = command.stringValueOfParameterNamed("note"); + if (StringUtils.isNotBlank(noteText)) { + final Note note = Note.savingNote(savingsAccount, noteText); + changes.put("note", noteText); + this.noteRepository.save(note); + } + } + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withEntityId(savingsId) // + .withOfficeId(savingsAccount.officeId()) // + .withClientId(savingsAccount.clientId()) // + .withGroupId(savingsAccount.groupId()) // + .withSavingsId(savingsId) // + .with(changes) // + .build(); + } + + @Transactional + @Override + public CommandProcessingResult rejectApplication(final Long savingsId, final JsonCommand command, + final DepositAccountType depositAccountType) { + + final AppUser currentUser = this.context.authenticatedUser(); + + this.savingsAccountApplicationTransitionApiJsonValidator.validateRejection(command.json()); + + final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(savingsId, depositAccountType); + checkClientOrGroupActive(savingsAccount); + + final Map changes = savingsAccount.rejectApplication(currentUser, command, DateUtils.getLocalDateOfTenant()); + if (!changes.isEmpty()) { + this.savingAccountRepository.save(savingsAccount); + + final String noteText = command.stringValueOfParameterNamed("note"); + if (StringUtils.isNotBlank(noteText)) { + final Note note = Note.savingNote(savingsAccount, noteText); + changes.put("note", noteText); + this.noteRepository.save(note); + } + } + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withEntityId(savingsId) // + .withOfficeId(savingsAccount.officeId()) // + .withClientId(savingsAccount.clientId()) // + .withGroupId(savingsAccount.groupId()) // + .withSavingsId(savingsId) // + .with(changes) // + .build(); + } + + @Transactional + @Override + public CommandProcessingResult applicantWithdrawsFromApplication(final Long savingsId, final JsonCommand command, + final DepositAccountType depositAccountType) { + final AppUser currentUser = this.context.authenticatedUser(); + + this.savingsAccountApplicationTransitionApiJsonValidator.validateApplicantWithdrawal(command.json()); + + final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(savingsId, depositAccountType); + checkClientOrGroupActive(savingsAccount); + + final Map changes = savingsAccount.applicantWithdrawsFromApplication(currentUser, command, + DateUtils.getLocalDateOfTenant()); + if (!changes.isEmpty()) { + this.savingAccountRepository.save(savingsAccount); + + final String noteText = command.stringValueOfParameterNamed("note"); + if (StringUtils.isNotBlank(noteText)) { + final Note note = Note.savingNote(savingsAccount, noteText); + changes.put("note", noteText); + this.noteRepository.save(note); + } + } + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withEntityId(savingsId) // + .withOfficeId(savingsAccount.officeId()) // + .withClientId(savingsAccount.clientId()) // + .withGroupId(savingsAccount.groupId()) // + .withSavingsId(savingsId) // + .with(changes) // + .build(); + } + + private void checkClientOrGroupActive(final SavingsAccount account) { + final Client client = account.getClient(); + if (client != null) { + if (client.isNotActive()) { throw new ClientNotActiveException(client.getId()); } + } + final Group group = account.group(); + if (group != null) { + if (group.isNotActive()) { + if (group.isCenter()) { throw new CenterNotActiveException(group.getId()); } + throw new GroupNotActiveException(group.getId()); + } + } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositProductReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositProductReadPlatformService.java new file mode 100644 index 000000000..0f388b196 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositProductReadPlatformService.java @@ -0,0 +1,23 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.service; + +import java.util.Collection; + +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.data.DepositProductData; + +public interface DepositProductReadPlatformService { + + Collection retrieveAll(final DepositAccountType depositAccountType); + + Collection retrieveAllForLookup(final DepositAccountType depositAccountType); + + DepositProductData retrieveOne(final DepositAccountType depositAccountType, Long productId); + + DepositProductData retrieveOneWithChartSlabs(final DepositAccountType depositAccountType, Long productId); + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositProductReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositProductReadPlatformServiceImpl.java new file mode 100644 index 000000000..d2005827d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositProductReadPlatformServiceImpl.java @@ -0,0 +1,363 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.service; + +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; + +import org.mifosplatform.accounting.common.AccountingEnumerations; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.infrastructure.core.domain.JdbcSupport; +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.interestratechart.data.InterestRateChartData; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartEnumerations; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartReadPlatformService; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.data.DepositProductData; +import org.mifosplatform.portfolio.savings.data.FixedDepositProductData; +import org.mifosplatform.portfolio.savings.data.RecurringDepositProductData; +import org.mifosplatform.portfolio.savings.exception.FixedDepositProductNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class DepositProductReadPlatformServiceImpl implements DepositProductReadPlatformService { + + private final PlatformSecurityContext context; + private final JdbcTemplate jdbcTemplate; + private final InterestRateChartReadPlatformService chartReadPlatformService; + private final FixedDepositProductMapper fixedDepositProductRowMapper = new FixedDepositProductMapper(); + private final RecurringDepositProductMapper recurringDepositProductRowMapper = new RecurringDepositProductMapper(); + private final DepositProductLookupMapper depositProductLookupsRowMapper = new DepositProductLookupMapper(); + + @Autowired + public DepositProductReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource, + final InterestRateChartReadPlatformService chartReadPlatformService) { + this.context = context; + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.chartReadPlatformService = chartReadPlatformService; + } + + @Override + public Collection retrieveAll(final DepositAccountType depositAccountType) { + + this.context.authenticatedUser(); + final DepositProductMapper depositProductMapper = this.getDepositProductMapper(depositAccountType); + if (depositProductMapper == null) return null; + + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("select "); + sqlBuilder.append(depositProductMapper.schema()); + sqlBuilder.append(" where sp.deposit_type_enum = ? "); + + return this.jdbcTemplate.query(sqlBuilder.toString(), depositProductMapper, new Object[] { depositAccountType.getValue() }); + } + + @Override + public Collection retrieveAllForLookup(final DepositAccountType depositAccountType) { + + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("select "); + sqlBuilder.append(this.depositProductLookupsRowMapper.schema()); + sqlBuilder.append(" where sp.deposit_type_enum = ? "); + + return this.jdbcTemplate.query(sqlBuilder.toString(), this.depositProductLookupsRowMapper, + new Object[] { depositAccountType.getValue() }); + } + + @Override + public DepositProductData retrieveOne(final DepositAccountType depositAccountType, final Long fixedDepositProductId) { + try { + this.context.authenticatedUser(); + + final DepositProductMapper depositProductMapper = this.getDepositProductMapper(depositAccountType); + if (depositProductMapper == null) return null; + + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("select "); + sqlBuilder.append(depositProductMapper.schema()); + sqlBuilder.append(" where sp.id = ? and sp.deposit_type_enum = ? "); + + return this.jdbcTemplate.queryForObject(sqlBuilder.toString(), depositProductMapper, new Object[] { fixedDepositProductId, + depositAccountType.getValue() }); + + } catch (final EmptyResultDataAccessException e) { + throw new FixedDepositProductNotFoundException(fixedDepositProductId); + } + } + + @Override + public DepositProductData retrieveOneWithChartSlabs(final DepositAccountType depositAccountType, Long depositProductId) { + DepositProductData depositProduct = this.retrieveOne(depositAccountType, depositProductId); + Collection charts = this.chartReadPlatformService.retrieveAllWithSlabsWithTemplate(depositProductId); + + if (depositAccountType.isFixedDeposit()) { + depositProduct = FixedDepositProductData.withInterestChart(depositProduct, charts); + } else if (depositAccountType.isRecurringDeposit()) { + depositProduct = RecurringDepositProductData.withInterestChart(depositProduct, charts); + } + + return depositProduct; + } + + private static abstract class DepositProductMapper implements RowMapper { + + private final String schemaSql; + + @Override + public abstract DepositProductData mapRow(ResultSet rs, int rowNum) throws SQLException; + + public DepositProductMapper() { + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("sp.id as id, sp.name as name, sp.short_name as shortName, sp.description as description, "); + sqlBuilder + .append("sp.currency_code as currencyCode, sp.currency_digits as currencyDigits, sp.currency_multiplesof as inMultiplesOf, "); + sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); + sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); + sqlBuilder.append("sp.nominal_annual_interest_rate as nominalAnnualInterestRate, "); + sqlBuilder.append("sp.interest_compounding_period_enum as compoundingInterestPeriodType, "); + sqlBuilder.append("sp.interest_posting_period_enum as interestPostingPeriodType, "); + sqlBuilder.append("sp.interest_calculation_type_enum as interestCalculationType, "); + sqlBuilder.append("sp.interest_calculation_days_in_year_type_enum as interestCalculationDaysInYearType, "); + sqlBuilder.append("sp.lockin_period_frequency as lockinPeriodFrequency,"); + sqlBuilder.append("sp.lockin_period_frequency_enum as lockinPeriodFrequencyType, "); + sqlBuilder.append("sp.accounting_type as accountingType "); + this.schemaSql = sqlBuilder.toString(); + } + + public String schema() { + return this.schemaSql; + } + + public DepositProductData mapRow(final ResultSet rs) throws SQLException { + + final Long id = rs.getLong("id"); + final String name = rs.getString("name"); + final String shortName = rs.getString("shortName"); + final String description = rs.getString("description"); + + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, + currencyDisplaySymbol, currencyNameCode); + final BigDecimal nominalAnnualInterestRate = rs.getBigDecimal("nominalAnnualInterestRate"); + + final Integer compoundingInterestPeriodTypeValue = JdbcSupport.getInteger(rs, "compoundingInterestPeriodType"); + final EnumOptionData compoundingInterestPeriodType = SavingsEnumerations + .compoundingInterestPeriodType(compoundingInterestPeriodTypeValue); + + final Integer interestPostingPeriodTypeValue = JdbcSupport.getInteger(rs, "interestPostingPeriodType"); + final EnumOptionData interestPostingPeriodType = SavingsEnumerations.interestPostingPeriodType(interestPostingPeriodTypeValue); + + final Integer interestCalculationTypeValue = JdbcSupport.getInteger(rs, "interestCalculationType"); + final EnumOptionData interestCalculationType = SavingsEnumerations.interestCalculationType(interestCalculationTypeValue); + + EnumOptionData interestCalculationDaysInYearType = null; + final Integer interestCalculationDaysInYearTypeValue = JdbcSupport.getInteger(rs, "interestCalculationDaysInYearType"); + if (interestCalculationDaysInYearTypeValue != null) { + interestCalculationDaysInYearType = SavingsEnumerations + .interestCalculationDaysInYearType(interestCalculationDaysInYearTypeValue); + } + + final Integer accountingRuleId = JdbcSupport.getInteger(rs, "accountingType"); + final EnumOptionData accountingRuleType = AccountingEnumerations.accountingRuleType(accountingRuleId); + + final Integer lockinPeriodFrequency = JdbcSupport.getInteger(rs, "lockinPeriodFrequency"); + EnumOptionData lockinPeriodFrequencyType = null; + final Integer lockinPeriodFrequencyTypeValue = JdbcSupport.getInteger(rs, "lockinPeriodFrequencyType"); + if (lockinPeriodFrequencyTypeValue != null) { + lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(lockinPeriodFrequencyTypeValue); + } + + return DepositProductData.instance(id, name, shortName, description, currency, nominalAnnualInterestRate, + compoundingInterestPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, + lockinPeriodFrequency, lockinPeriodFrequencyType, accountingRuleType); + } + } + + private static class FixedDepositProductMapper extends DepositProductMapper { + + private final String schemaSql; + + public FixedDepositProductMapper() { + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append(super.schema()); + sqlBuilder.append(", dptp.interest_free_period_applicable as interestFreePeriodApplicable, "); + sqlBuilder.append("dptp.interest_free_from_period as interestFreeFromPeriod, "); + sqlBuilder.append("dptp.interest_free_to_period as interestFreeToPeriod, "); + sqlBuilder.append("dptp.interest_free_period_frequency_enum as interestFreePeriodFrequencyId, "); + sqlBuilder.append("dptp.pre_closure_penal_applicable as preClosurePenalApplicable, "); + sqlBuilder.append("dptp.pre_closure_penal_interest as preClosurePenalInterest, "); + sqlBuilder.append("dptp.pre_closure_penal_interest_on_enum as preClosurePenalInterestOnId, "); + sqlBuilder.append("dptp.min_deposit_term as minDepositTerm, "); + sqlBuilder.append("dptp.max_deposit_term as maxDepositTerm, "); + sqlBuilder.append("dptp.min_deposit_term_type_enum as minDepositTermTypeId, "); + sqlBuilder.append("dptp.max_deposit_term_type_enum as maxDepositTermTypeId, "); + sqlBuilder.append("dptp.in_multiples_of_deposit_term as inMultiplesOfDepositTerm, "); + sqlBuilder.append("dptp.in_multiples_of_deposit_term_type_enum as inMultiplesOfDepositTermTypeId "); + sqlBuilder.append("from m_savings_product sp "); + sqlBuilder.append("left join m_deposit_product_term_and_preclosure dptp on sp.id=dptp.savings_product_id "); + sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code "); + + this.schemaSql = sqlBuilder.toString(); + } + + @Override + public String schema() { + return this.schemaSql; + } + + @Override + public FixedDepositProductData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final DepositProductData depositProductData = super.mapRow(rs); + + final boolean interestFreePeriodApplicable = rs.getBoolean("interestFreePeriodApplicable"); + final Integer interestFreeFromPeriod = JdbcSupport.getInteger(rs, "interestFreeFromPeriod"); + final Integer interestFreeToPeriod = JdbcSupport.getInteger(rs, "interestFreeToPeriod"); + final Integer periodFrequencyTypeId = JdbcSupport.getInteger(rs, "interestFreePeriodFrequencyId"); + final EnumOptionData interestFreePeriodFrequencyType = (periodFrequencyTypeId == null) ? null : InterestRateChartEnumerations + .periodType(periodFrequencyTypeId); + final boolean preClosurePenalApplicable = rs.getBoolean("preClosurePenalApplicable"); + final BigDecimal preClosurePenalInterest = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "preClosurePenalInterest"); + final Integer preClosurePenalInterestOnTypeId = JdbcSupport.getInteger(rs, "preClosurePenalInterestOnId"); + final EnumOptionData preClosurePenalInterestOnType = (preClosurePenalInterestOnTypeId == null) ? null : SavingsEnumerations + .preClosurePenaltyInterestOnType(preClosurePenalInterestOnTypeId); + + final Integer minDepositTerm = JdbcSupport.getInteger(rs, "minDepositTerm"); + final Integer maxDepositTerm = JdbcSupport.getInteger(rs, "maxDepositTerm"); + final Integer minDepositTermTypeId = JdbcSupport.getInteger(rs, "minDepositTermTypeId"); + final EnumOptionData minDepositTermType = (minDepositTermTypeId == null) ? null : SavingsEnumerations + .depositTermFrequencyType(minDepositTermTypeId); + final Integer maxDepositTermTypeId = JdbcSupport.getInteger(rs, "maxDepositTermTypeId"); + final EnumOptionData maxDepositTermType = (maxDepositTermTypeId == null) ? null : SavingsEnumerations + .depositTermFrequencyType(maxDepositTermTypeId); + final Integer inMultiplesOfDepositTerm = JdbcSupport.getInteger(rs, "inMultiplesOfDepositTerm"); + final Integer inMultiplesOfDepositTermTypeId = JdbcSupport.getInteger(rs, "inMultiplesOfDepositTermTypeId"); + final EnumOptionData inMultiplesOfDepositTermType = (inMultiplesOfDepositTermTypeId == null) ? null : SavingsEnumerations + .depositTermFrequencyType(inMultiplesOfDepositTermTypeId); + + return FixedDepositProductData.instance(depositProductData, interestFreePeriodApplicable, interestFreeFromPeriod, + interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest, + preClosurePenalInterestOnType, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, + inMultiplesOfDepositTermType); + } + } + + private static class RecurringDepositProductMapper extends DepositProductMapper { + + private final String schemaSql; + + public RecurringDepositProductMapper() { + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append(super.schema()); + sqlBuilder.append(", dptp.interest_free_period_applicable as interestFreePeriodApplicable, "); + sqlBuilder.append("dptp.interest_free_from_period as interestFreeFromPeriod, "); + sqlBuilder.append("dptp.interest_free_to_period as interestFreeToPeriod, "); + sqlBuilder.append("dptp.interest_free_period_frequency_enum as interestFreePeriodFrequencyId, "); + sqlBuilder.append("dptp.pre_closure_penal_applicable as preClosurePenalApplicable, "); + sqlBuilder.append("dptp.pre_closure_penal_interest as preClosurePenalInterest, "); + sqlBuilder.append("dptp.pre_closure_penal_interest_on_enum as preClosurePenalInterestOnId, "); + sqlBuilder.append("dprd.recurring_deposit_type_enum as recurringDepositTypeId, "); + sqlBuilder.append("dprd.recurring_deposit_frequency as recurringDepositFrequency, "); + sqlBuilder.append("dprd.recurring_deposit_frequency_type_enum as recurringDepositFrequencyTypeId, "); + sqlBuilder.append("dptp.min_deposit_term as minDepositTerm, "); + sqlBuilder.append("dptp.max_deposit_term as maxDepositTerm, "); + sqlBuilder.append("dptp.min_deposit_term_type_enum as minDepositTermTypeId, "); + sqlBuilder.append("dptp.max_deposit_term_type_enum as maxDepositTermTypeId, "); + sqlBuilder.append("dptp.in_multiples_of_deposit_term as inMultiplesOfDepositTerm, "); + sqlBuilder.append("dptp.in_multiples_of_deposit_term_type_enum as inMultiplesOfDepositTermTypeId "); + sqlBuilder.append("from m_savings_product sp "); + sqlBuilder.append("left join m_deposit_product_term_and_preclosure dptp on sp.id=dptp.savings_product_id "); + sqlBuilder.append("left join m_deposit_product_recurring_detail dprd on sp.id=dprd.savings_product_id "); + sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code "); + + this.schemaSql = sqlBuilder.toString(); + } + + @Override + public String schema() { + return this.schemaSql; + } + + @Override + public RecurringDepositProductData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final DepositProductData depositProductData = super.mapRow(rs); + + final boolean interestFreePeriodApplicable = rs.getBoolean("interestFreePeriodApplicable"); + final Integer interestFreeFromPeriod = JdbcSupport.getInteger(rs, "interestFreeFromPeriod"); + final Integer interestFreeToPeriod = JdbcSupport.getInteger(rs, "interestFreeToPeriod"); + final Integer periodFrequencyTypeId = JdbcSupport.getInteger(rs, "interestFreePeriodFrequencyId"); + final EnumOptionData interestFreePeriodFrequencyType = (periodFrequencyTypeId == null) ? null : InterestRateChartEnumerations + .periodType(periodFrequencyTypeId); + final boolean preClosurePenalApplicable = rs.getBoolean("preClosurePenalApplicable"); + final BigDecimal preClosurePenalInterest = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "preClosurePenalInterest"); + final Integer preClosurePenalInterestOnTypeId = JdbcSupport.getInteger(rs, "preClosurePenalInterestOnId"); + final EnumOptionData preClosurePenalInterestOnType = (preClosurePenalInterestOnTypeId == null) ? null : SavingsEnumerations + .preClosurePenaltyInterestOnType(preClosurePenalInterestOnTypeId); + final Integer recurringDepositTypeId = JdbcSupport.getInteger(rs, "recurringDepositTypeId"); + final EnumOptionData recurringDepositType = (recurringDepositTypeId == null) ? null : SavingsEnumerations + .recurringDepositType(recurringDepositTypeId); + final Integer recurringDepositFrequency = JdbcSupport.getInteger(rs, "recurringDepositFrequency"); + final Integer recurringDepositFrequencyTypeId = JdbcSupport.getInteger(rs, "recurringDepositFrequencyTypeId"); + final EnumOptionData recurringDepositFrequencyType = (recurringDepositFrequencyTypeId == null) ? null : SavingsEnumerations + .recurringDepositFrequencyType(recurringDepositFrequencyTypeId); + + final Integer minDepositTerm = JdbcSupport.getInteger(rs, "minDepositTerm"); + final Integer maxDepositTerm = JdbcSupport.getInteger(rs, "maxDepositTerm"); + final Integer minDepositTermTypeId = JdbcSupport.getInteger(rs, "minDepositTermTypeId"); + final EnumOptionData minDepositTermType = (minDepositTermTypeId == null) ? null : SavingsEnumerations + .depositTermFrequencyType(minDepositTermTypeId); + final Integer maxDepositTermTypeId = JdbcSupport.getInteger(rs, "maxDepositTermTypeId"); + final EnumOptionData maxDepositTermType = (maxDepositTermTypeId == null) ? null : SavingsEnumerations + .depositTermFrequencyType(maxDepositTermTypeId); + final Integer inMultiplesOfDepositTerm = JdbcSupport.getInteger(rs, "inMultiplesOfDepositTerm"); + final Integer inMultiplesOfDepositTermTypeId = JdbcSupport.getInteger(rs, "inMultiplesOfDepositTermTypeId"); + final EnumOptionData inMultiplesOfDepositTermType = (inMultiplesOfDepositTermTypeId == null) ? null : SavingsEnumerations + .depositTermFrequencyType(inMultiplesOfDepositTermTypeId); + + return RecurringDepositProductData.instance(depositProductData, interestFreePeriodApplicable, interestFreeFromPeriod, + interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest, + preClosurePenalInterestOnType, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, + inMultiplesOfDepositTermType, recurringDepositType, recurringDepositFrequency, recurringDepositFrequencyType); + } + } + + private static final class DepositProductLookupMapper implements RowMapper { + + public String schema() { + return " sp.id as id, sp.name as name from m_savings_product sp "; + } + + @Override + public DepositProductData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = rs.getLong("id"); + final String name = rs.getString("name"); + + return DepositProductData.lookup(id, name); + } + } + + private DepositProductMapper getDepositProductMapper(final DepositAccountType depositAccountType) { + if (depositAccountType.isFixedDeposit()) { + return this.fixedDepositProductRowMapper; + } else if (depositAccountType.isRecurringDeposit()) { return this.recurringDepositProductRowMapper; } + return null; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositsDropdownReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositsDropdownReadPlatformService.java new file mode 100644 index 000000000..e10a7acf6 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositsDropdownReadPlatformService.java @@ -0,0 +1,31 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.service; + +import java.util.Collection; + +import org.mifosplatform.infrastructure.core.data.EnumOptionData; + +public interface DepositsDropdownReadPlatformService { + + // fixed deposit product dropdowns + + Collection retrieveInterestFreePeriodFrequencyTypeOptions(); + + Collection retrievePreClosurePenalInterestOnTypeOptions(); + + // RECURRING deposit product dropdowns + Collection retrieveRecurringDepositTypeOptions(); + + Collection retrieveRecurringDepositFrequencyTypeOptions(); + + Collection retrieveDepositTermTypeOptions(); + + Collection retrieveInMultiplesOfDepositTermTypeOptions(); + + Collection retrieveDepositPeriodFrequencyOptions(); + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositsDropdownReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositsDropdownReadPlatformServiceImpl.java new file mode 100644 index 000000000..ce19eb09c --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositsDropdownReadPlatformServiceImpl.java @@ -0,0 +1,56 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.service; + +import java.util.Collection; + +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.portfolio.interestratechart.InterestRateChartPeriodType; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartEnumerations; +import org.mifosplatform.portfolio.savings.PreClosurePenalInterestOnType; +import org.mifosplatform.portfolio.savings.RecurringDepositType; +import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; +import org.springframework.stereotype.Service; + +@Service +public class DepositsDropdownReadPlatformServiceImpl implements DepositsDropdownReadPlatformService { + + @Override + public Collection retrieveInterestFreePeriodFrequencyTypeOptions() { + return InterestRateChartEnumerations.periodType(InterestRateChartPeriodType.values()); + } + + @Override + public Collection retrievePreClosurePenalInterestOnTypeOptions() { + return SavingsEnumerations.preClosurePenaltyInterestOnType(PreClosurePenalInterestOnType.values()); + } + + @Override + public Collection retrieveRecurringDepositTypeOptions() { + return SavingsEnumerations.recurringDepositType(RecurringDepositType.values()); + } + + @Override + public Collection retrieveRecurringDepositFrequencyTypeOptions() { + return SavingsEnumerations.recurringDepositFrequencyType(SavingsPeriodFrequencyType.values()); + } + + @Override + public Collection retrieveDepositTermTypeOptions() { + return SavingsEnumerations.recurringDepositFrequencyType(SavingsPeriodFrequencyType.values()); + } + + @Override + public Collection retrieveDepositPeriodFrequencyOptions() { + return SavingsEnumerations.depositPeriodFrequency(SavingsPeriodFrequencyType.values()); + } + + @Override + public Collection retrieveInMultiplesOfDepositTermTypeOptions() { + return SavingsEnumerations.recurringDepositFrequencyType(SavingsPeriodFrequencyType.values()); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/FixedDepositProductWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/FixedDepositProductWritePlatformService.java new file mode 100644 index 000000000..fa1e55af0 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/FixedDepositProductWritePlatformService.java @@ -0,0 +1,18 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.service; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; + +public interface FixedDepositProductWritePlatformService { + + CommandProcessingResult create(JsonCommand command); + + CommandProcessingResult update(Long productId, JsonCommand command); + + CommandProcessingResult delete(Long productId); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/FixedDepositProductWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/FixedDepositProductWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 000000000..3b6a485fe --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/FixedDepositProductWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,168 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.service; + +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountingRuleParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargesParamName; + +import java.util.Map; +import java.util.Set; + +import org.mifosplatform.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.charge.domain.Charge; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartAssembler; +import org.mifosplatform.portfolio.savings.data.DepositProductDataValidator; +import org.mifosplatform.portfolio.savings.domain.DepositProductAssembler; +import org.mifosplatform.portfolio.savings.domain.FixedDepositProduct; +import org.mifosplatform.portfolio.savings.domain.FixedDepositProductRepository; +import org.mifosplatform.portfolio.savings.exception.FixedDepositProductNotFoundException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class FixedDepositProductWritePlatformServiceJpaRepositoryImpl implements FixedDepositProductWritePlatformService { + + private final Logger logger; + private final PlatformSecurityContext context; + private final FixedDepositProductRepository fixedDepositProductRepository; + private final DepositProductDataValidator fromApiJsonDataValidator; + private final DepositProductAssembler depositProductAssembler; + private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService; + private final InterestRateChartAssembler chartAssembler; + + @Autowired + public FixedDepositProductWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, + final FixedDepositProductRepository fixedDepositProductRepository, + final DepositProductDataValidator fromApiJsonDataValidator, + final DepositProductAssembler depositProductAssembler, + final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService, + final InterestRateChartAssembler chartAssembler) { + this.context = context; + this.fixedDepositProductRepository = fixedDepositProductRepository; + this.fromApiJsonDataValidator = fromApiJsonDataValidator; + this.depositProductAssembler = depositProductAssembler; + this.logger = LoggerFactory.getLogger(FixedDepositProductWritePlatformServiceJpaRepositoryImpl.class); + this.accountMappingWritePlatformService = accountMappingWritePlatformService; + this.chartAssembler = chartAssembler; + } + + @Transactional + @Override + public CommandProcessingResult create(final JsonCommand command) { + + try { + this.fromApiJsonDataValidator.validateForFixedDepositCreate(command.json()); + + final FixedDepositProduct product = this.depositProductAssembler.assembleFixedDepositProduct(command); + + this.fixedDepositProductRepository.save(product); + + // save accounting mappings + this.accountMappingWritePlatformService.createSavingProductToGLAccountMapping(product.getId(), command); + + return new CommandProcessingResultBuilder() // + .withEntityId(product.getId()) // + .build(); + } catch (final DataAccessException e) { + handleDataIntegrityIssues(command, e); + return CommandProcessingResult.empty(); + } + } + + @Transactional + @Override + public CommandProcessingResult update(final Long productId, final JsonCommand command) { + + try { + this.context.authenticatedUser(); + this.fromApiJsonDataValidator.validateForFixedDepositUpdate(command.json()); + + final FixedDepositProduct product = this.fixedDepositProductRepository.findOne(productId); + if (product == null) { throw new FixedDepositProductNotFoundException(productId); } + product.setHelpers(this.chartAssembler); + + final Map changes = product.update(command); + + if (changes.containsKey(chargesParamName)) { + final Set savingsProductCharges = this.depositProductAssembler.assembleListOfSavingsProductCharges(command, + product.currency().getCode()); + final boolean updated = product.update(savingsProductCharges); + if (!updated) { + changes.remove(chargesParamName); + } + } + + // accounting related changes + final boolean accountingTypeChanged = changes.containsKey(accountingRuleParamName); + final Map accountingMappingChanges = this.accountMappingWritePlatformService + .updateSavingsProductToGLAccountMapping(product.getId(), command, accountingTypeChanged, product.getAccountingType()); + changes.putAll(accountingMappingChanges); + + if (!changes.isEmpty()) { + this.fixedDepositProductRepository.saveAndFlush(product); + } + + return new CommandProcessingResultBuilder() // + .withEntityId(product.getId()) // + .with(changes).build(); + } catch (final DataAccessException e) { + handleDataIntegrityIssues(command, e); + return CommandProcessingResult.empty(); + } + } + + @Transactional + @Override + public CommandProcessingResult delete(final Long productId) { + + this.context.authenticatedUser(); + final FixedDepositProduct product = this.fixedDepositProductRepository.findOne(productId); + if (product == null) { throw new FixedDepositProductNotFoundException(productId); } + + this.fixedDepositProductRepository.delete(product); + + return new CommandProcessingResultBuilder() // + .withEntityId(product.getId()) // + .build(); + } + + /* + * Guaranteed to throw an exception no matter what the data integrity issue + * is. + */ + private void handleDataIntegrityIssues(final JsonCommand command, final DataAccessException dae) { + + final Throwable realCause = dae.getMostSpecificCause(); + if (realCause.getMessage().contains("sp_unq_name")) { + + final String name = command.stringValueOfParameterNamed("name"); + throw new PlatformDataIntegrityException("error.msg.product.savings.duplicate.name", "Savings product with name `" + name + + "` already exists", "name", name); + } else if (realCause.getMessage().contains("sp_unq_short_name")) { + + final String shortName = command.stringValueOfParameterNamed("shortName"); + throw new PlatformDataIntegrityException("error.msg.product.savings.duplicate.short.name", "Savings product with short name `" + + shortName + "` already exists", "shortName", shortName); + } + + logAsErrorUnexpectedDataIntegrityException(dae); + throw new PlatformDataIntegrityException("error.msg.savingsproduct.unknown.data.integrity.issue", + "Unknown data integrity issue with resource."); + } + + private void logAsErrorUnexpectedDataIntegrityException(final DataAccessException dae) { + this.logger.error(dae.getMessage(), dae); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/RecurringDepositProductWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/RecurringDepositProductWritePlatformService.java new file mode 100644 index 000000000..46d54e20d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/RecurringDepositProductWritePlatformService.java @@ -0,0 +1,18 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.service; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; + +public interface RecurringDepositProductWritePlatformService { + + CommandProcessingResult create(JsonCommand command); + + CommandProcessingResult update(Long productId, JsonCommand command); + + CommandProcessingResult delete(Long productId); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/RecurringDepositProductWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/RecurringDepositProductWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 000000000..65d32f7ba --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/RecurringDepositProductWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,168 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.service; + +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountingRuleParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargesParamName; + +import java.util.Map; +import java.util.Set; + +import org.mifosplatform.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.charge.domain.Charge; +import org.mifosplatform.portfolio.interestratechart.service.InterestRateChartAssembler; +import org.mifosplatform.portfolio.savings.data.DepositProductDataValidator; +import org.mifosplatform.portfolio.savings.domain.DepositProductAssembler; +import org.mifosplatform.portfolio.savings.domain.RecurringDepositProduct; +import org.mifosplatform.portfolio.savings.domain.RecurringDepositProductRepository; +import org.mifosplatform.portfolio.savings.exception.RecurringDepositProductNotFoundException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class RecurringDepositProductWritePlatformServiceJpaRepositoryImpl implements RecurringDepositProductWritePlatformService { + + private final Logger logger; + private final PlatformSecurityContext context; + private final RecurringDepositProductRepository recurringDepositProductRepository; + private final DepositProductDataValidator fromApiJsonDataValidator; + private final DepositProductAssembler depositProductAssembler; + private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService; + private final InterestRateChartAssembler chartAssembler; + + @Autowired + public RecurringDepositProductWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, + final RecurringDepositProductRepository recurringDepositProductRepository, + final DepositProductDataValidator fromApiJsonDataValidator, + final DepositProductAssembler depositProductAssembler, + final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService, + final InterestRateChartAssembler chartAssembler) { + this.context = context; + this.recurringDepositProductRepository = recurringDepositProductRepository; + this.fromApiJsonDataValidator = fromApiJsonDataValidator; + this.depositProductAssembler = depositProductAssembler; + this.logger = LoggerFactory.getLogger(RecurringDepositProductWritePlatformServiceJpaRepositoryImpl.class); + this.accountMappingWritePlatformService = accountMappingWritePlatformService; + this.chartAssembler = chartAssembler; + } + + @Transactional + @Override + public CommandProcessingResult create(final JsonCommand command) { + + try { + this.fromApiJsonDataValidator.validateForRecurringDepositCreate(command.json()); + + final RecurringDepositProduct product = this.depositProductAssembler.assembleRecurringDepositProduct(command); + + this.recurringDepositProductRepository.save(product); + + // save accounting mappings + this.accountMappingWritePlatformService.createSavingProductToGLAccountMapping(product.getId(), command); + + return new CommandProcessingResultBuilder() // + .withEntityId(product.getId()) // + .build(); + } catch (final DataAccessException e) { + handleDataIntegrityIssues(command, e); + return CommandProcessingResult.empty(); + } + } + + @Transactional + @Override + public CommandProcessingResult update(final Long productId, final JsonCommand command) { + + try { + this.context.authenticatedUser(); + this.fromApiJsonDataValidator.validateForRecurringDepositUpdate(command.json()); + + final RecurringDepositProduct product = this.recurringDepositProductRepository.findOne(productId); + if (product == null) { throw new RecurringDepositProductNotFoundException(productId); } + product.setHelpers(this.chartAssembler); + + final Map changes = product.update(command); + + if (changes.containsKey(chargesParamName)) { + final Set savingsProductCharges = this.depositProductAssembler.assembleListOfSavingsProductCharges(command, + product.currency().getCode()); + final boolean updated = product.update(savingsProductCharges); + if (!updated) { + changes.remove(chargesParamName); + } + } + + // accounting related changes + final boolean accountingTypeChanged = changes.containsKey(accountingRuleParamName); + final Map accountingMappingChanges = this.accountMappingWritePlatformService + .updateSavingsProductToGLAccountMapping(product.getId(), command, accountingTypeChanged, product.getAccountingType()); + changes.putAll(accountingMappingChanges); + + if (!changes.isEmpty()) { + this.recurringDepositProductRepository.saveAndFlush(product); + } + + return new CommandProcessingResultBuilder() // + .withEntityId(product.getId()) // + .with(changes).build(); + } catch (final DataAccessException e) { + handleDataIntegrityIssues(command, e); + return CommandProcessingResult.empty(); + } + } + + @Transactional + @Override + public CommandProcessingResult delete(final Long productId) { + + this.context.authenticatedUser(); + final RecurringDepositProduct product = this.recurringDepositProductRepository.findOne(productId); + if (product == null) { throw new RecurringDepositProductNotFoundException(productId); } + + this.recurringDepositProductRepository.delete(product); + + return new CommandProcessingResultBuilder() // + .withEntityId(product.getId()) // + .build(); + } + + /* + * Guaranteed to throw an exception no matter what the data integrity issue + * is. + */ + private void handleDataIntegrityIssues(final JsonCommand command, final DataAccessException dae) { + + final Throwable realCause = dae.getMostSpecificCause(); + if (realCause.getMessage().contains("sp_unq_name")) { + + final String name = command.stringValueOfParameterNamed("name"); + throw new PlatformDataIntegrityException("error.msg.product.savings.duplicate.name", "Recurring Deposit product with name `" + name + + "` already exists", "name", name); + } else if (realCause.getMessage().contains("sp_unq_short_name")) { + + final String shortName = command.stringValueOfParameterNamed("shortName"); + throw new PlatformDataIntegrityException("error.msg.product.savings.duplicate.short.name", "Recurring Deposit product with short name `" + + shortName + "` already exists", "shortName", shortName); + } + + logAsErrorUnexpectedDataIntegrityException(dae); + throw new PlatformDataIntegrityException("error.msg.savingsproduct.unknown.data.integrity.issue", + "Unknown data integrity issue with resource."); + } + + private void logAsErrorUnexpectedDataIntegrityException(final DataAccessException dae) { + this.logger.error(dae.getMessage(), dae); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java index b9bce25af..d331f14f5 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java @@ -9,6 +9,7 @@ import java.util.Collection; import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.portfolio.group.service.SearchParameters; +import org.mifosplatform.portfolio.savings.DepositAccountType; import org.mifosplatform.portfolio.savings.data.SavingsAccountData; import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionData; @@ -17,19 +18,21 @@ public interface SavingsAccountReadPlatformService { Page retrieveAll(SearchParameters searchParameters); Collection retrieveAllForLookup(Long clientId); + + Collection retrieveActiveForLookup(Long clientId, DepositAccountType depositAccountType); SavingsAccountData retrieveOne(Long savingsId); SavingsAccountData retrieveTemplate(Long clientId, Long groupId, Long productId, boolean staffInSelectedOfficeOnly); - SavingsAccountTransactionData retrieveDepositTransactionTemplate(Long savingsId); + SavingsAccountTransactionData retrieveDepositTransactionTemplate(Long savingsId, DepositAccountType depositAccountType); - Collection retrieveAllTransactions(Long savingsId); + Collection retrieveAllTransactions(Long savingsId, DepositAccountType depositAccountType); // Collection // retrieveAccountsWithAnnualFeeDue(); - SavingsAccountTransactionData retrieveSavingsTransaction(Long savingsId, Long transactionId); + SavingsAccountTransactionData retrieveSavingsTransaction(Long savingsId, Long transactionId, DepositAccountType depositAccountType); Collection retrieveForLookup(Long clientId,Boolean overdraft); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java index a987ca57f..11217704d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java @@ -34,6 +34,7 @@ import org.mifosplatform.portfolio.group.data.GroupGeneralData; import org.mifosplatform.portfolio.group.service.GroupReadPlatformService; import org.mifosplatform.portfolio.group.service.SearchParameters; import org.mifosplatform.portfolio.paymentdetail.data.PaymentDetailData; +import org.mifosplatform.portfolio.savings.DepositAccountType; import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType; import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; @@ -106,6 +107,16 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final Object[] queryParameters = new Object[] { clientId }; return this.jdbcTemplate.query(sqlBuilder.toString(), this.savingAccountMapper, queryParameters); } + + @Override + public Collection retrieveActiveForLookup(final Long clientId, DepositAccountType depositAccountType) { + + final StringBuilder sqlBuilder = new StringBuilder("select " + this.savingAccountMapper.schema()); + sqlBuilder.append(" where sa.client_id = ? and sa.status_enum = 300 and sa.deposit_type_enum = ? "); + + final Object[] queryParameters = new Object[] { clientId, depositAccountType.getValue() }; + return this.jdbcTemplate.query(sqlBuilder.toString(), this.savingAccountMapper, queryParameters); + } @Override public Page retrieveAll(final SearchParameters searchParameters) { @@ -572,32 +583,32 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead } @Override - public SavingsAccountTransactionData retrieveDepositTransactionTemplate(final Long savingsId) { + public SavingsAccountTransactionData retrieveDepositTransactionTemplate(final Long savingsId, final DepositAccountType depositAccountType) { try { - final String sql = "select " + this.transactionTemplateMapper.schema() + " where sa.id = ?"; + final String sql = "select " + this.transactionTemplateMapper.schema() + " where sa.id = ? and sa.deposit_type_enum = ?"; - return this.jdbcTemplate.queryForObject(sql, this.transactionTemplateMapper, new Object[] { savingsId }); + return this.jdbcTemplate.queryForObject(sql, this.transactionTemplateMapper, new Object[] { savingsId, depositAccountType.getValue()}); } catch (final EmptyResultDataAccessException e) { throw new SavingsAccountNotFoundException(savingsId); } } @Override - public Collection retrieveAllTransactions(final Long savingsId) { + public Collection retrieveAllTransactions(final Long savingsId, DepositAccountType depositAccountType) { final String sql = "select " + this.transactionsMapper.schema() - + " where sa.id = ? order by tr.transaction_date DESC, tr.created_date DESC, tr.id DESC"; + + " where sa.id = ? and sa.deposit_type_enum = ? order by tr.transaction_date DESC, tr.created_date DESC, tr.id DESC"; - return this.jdbcTemplate.query(sql, this.transactionsMapper, new Object[] { savingsId }); + return this.jdbcTemplate.query(sql, this.transactionsMapper, new Object[] { savingsId, depositAccountType.getValue() }); } @Override - public SavingsAccountTransactionData retrieveSavingsTransaction(final Long savingsId, final Long transactionId) { + public SavingsAccountTransactionData retrieveSavingsTransaction(final Long savingsId, final Long transactionId, DepositAccountType depositAccountType) { - final String sql = "select " + this.transactionsMapper.schema() + " where sa.id = ? and tr.id= ?"; + final String sql = "select " + this.transactionsMapper.schema() + " where sa.id = ? and sa.deposit_type_enum = ? and tr.id= ?"; - return this.jdbcTemplate.queryForObject(sql, this.transactionsMapper, new Object[] { savingsId, transactionId }); + return this.jdbcTemplate.queryForObject(sql, this.transactionsMapper, new Object[] { savingsId, depositAccountType.getValue(), transactionId }); } /* diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl.java index 1f3ecf6fd..905ed6df0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl.java @@ -175,7 +175,7 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId); checkClientOrGroupActive(account); account.modifyApplication(command, changes); - account.validateNewApplicationState(DateUtils.getLocalDateOfTenant()); + account.validateNewApplicationState(DateUtils.getLocalDateOfTenant(), SAVINGS_ACCOUNT_RESOURCE_NAME); account.validateAccountValuesWithProduct(); if (!changes.isEmpty()) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsEnumerations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsEnumerations.java index 01435de7f..4af80bfe5 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsEnumerations.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsEnumerations.java @@ -5,7 +5,14 @@ */ package org.mifosplatform.portfolio.savings.service; +import java.util.ArrayList; +import java.util.List; + import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.portfolio.savings.DepositAccountOnClosureType; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.PreClosurePenalInterestOnType; +import org.mifosplatform.portfolio.savings.RecurringDepositType; import org.mifosplatform.portfolio.savings.SavingsAccountTransactionType; import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType; @@ -133,59 +140,70 @@ public class SavingsEnumerations { final boolean isWithdrawnByApplicant = type.isApplicationWithdrawnByApplicant(); final boolean isActive = type.isActive(); final boolean isClosed = type.isClosed(); + final boolean isPrematureClosed = type.isPreMatureClosure(); final boolean isTransferInProgress = type.isTransferInProgress(); final boolean isTransferOnHold = type.isTransferOnHold(); SavingsAccountStatusEnumData optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.INVALID.getValue().longValue(), SavingsAccountStatusType.INVALID.getCode(), "Invalid", submittedAndPendingApproval, isApproved, isRejected, - isWithdrawnByApplicant, isActive, isClosed, isTransferInProgress, isTransferOnHold); + isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold); switch (type) { case INVALID: optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.INVALID.getValue().longValue(), SavingsAccountStatusType.INVALID.getCode(), "Invalid", submittedAndPendingApproval, isApproved, isRejected, - isWithdrawnByApplicant, isActive, isClosed, isTransferInProgress, isTransferOnHold); + isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold); break; case SUBMITTED_AND_PENDING_APPROVAL: optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.getValue() .longValue(), SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.getCode(), "Submitted and pending approval", - submittedAndPendingApproval, isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed, + submittedAndPendingApproval, isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold); break; case REJECTED: optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.REJECTED.getValue().longValue(), SavingsAccountStatusType.REJECTED.getCode(), "Rejected", submittedAndPendingApproval, isApproved, isRejected, - isWithdrawnByApplicant, isActive, isClosed, isTransferInProgress, isTransferOnHold); + isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold); break; case WITHDRAWN_BY_APPLICANT: optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.WITHDRAWN_BY_APPLICANT.getValue().longValue(), SavingsAccountStatusType.WITHDRAWN_BY_APPLICANT.getCode(), "Withdrawn by applicant", submittedAndPendingApproval, - isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed, isTransferInProgress, isTransferOnHold); + isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold); break; case APPROVED: optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.APPROVED.getValue().longValue(), SavingsAccountStatusType.APPROVED.getCode(), "Approved", submittedAndPendingApproval, isApproved, isRejected, - isWithdrawnByApplicant, isActive, isClosed, isTransferInProgress, isTransferOnHold); + isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold); break; case ACTIVE: optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.ACTIVE.getValue().longValue(), SavingsAccountStatusType.ACTIVE.getCode(), "Active", submittedAndPendingApproval, isApproved, isRejected, - isWithdrawnByApplicant, isActive, isClosed, isTransferInProgress, isTransferOnHold); + isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold); break; case CLOSED: optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.CLOSED.getValue().longValue(), SavingsAccountStatusType.CLOSED.getCode(), "Closed", submittedAndPendingApproval, isApproved, isRejected, - isWithdrawnByApplicant, isActive, isClosed, isTransferInProgress, isTransferOnHold); + isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold); break; case TRANSFER_IN_PROGRESS: optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue().longValue(), SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getCode(), "Transfer in progress", submittedAndPendingApproval, - isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed, isTransferInProgress, isTransferOnHold); + isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold); break; case TRANSFER_ON_HOLD: optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue().longValue(), SavingsAccountStatusType.TRANSFER_ON_HOLD.getCode(), "Transfer in progress", submittedAndPendingApproval, - isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed, isTransferInProgress, isTransferOnHold); + isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold); + break; + case PRE_MATURE_CLOSURE: + optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.PRE_MATURE_CLOSURE.getValue().longValue(), + SavingsAccountStatusType.PRE_MATURE_CLOSURE.getCode(), "Premature Closed", submittedAndPendingApproval, + isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold); + break; + case MATURED: + optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.MATURED.getValue().longValue(), + SavingsAccountStatusType.MATURED.getCode(), "Matured", submittedAndPendingApproval, + isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold); break; default: break; @@ -362,4 +380,305 @@ public class SavingsEnumerations { return optionData; } + + public static EnumOptionData preClosurePenaltyInterestOnType(final Integer type) { + return preClosurePenaltyInterestOnType(PreClosurePenalInterestOnType.fromInt(type)); + } + + public static EnumOptionData preClosurePenaltyInterestOnType(final PreClosurePenalInterestOnType type) { + EnumOptionData optionData = new EnumOptionData(PreClosurePenalInterestOnType.INVALID.getValue().longValue(), + PreClosurePenalInterestOnType.INVALID.getCode(), "Invalid"); + + switch (type) { + case INVALID: + break; + case WHOLE_TERM: + optionData = new EnumOptionData(PreClosurePenalInterestOnType.WHOLE_TERM.getValue().longValue(), + PreClosurePenalInterestOnType.WHOLE_TERM.getCode(), "Whole term"); + break; + case TILL_PREMATURE_WITHDRAWAL: + optionData = new EnumOptionData(PreClosurePenalInterestOnType.TILL_PREMATURE_WITHDRAWAL.getValue().longValue(), + PreClosurePenalInterestOnType.TILL_PREMATURE_WITHDRAWAL.getCode(), "Till Premature Withdrawal"); + break; + } + + return optionData; + } + + public static List preClosurePenaltyInterestOnType(final PreClosurePenalInterestOnType[] types) { + final List optionDatas = new ArrayList(); + for (final PreClosurePenalInterestOnType type : types) { + if (!type.isInvalid()) { + optionDatas.add(preClosurePenaltyInterestOnType(type)); + } + } + return optionDatas; + } + + public static EnumOptionData recurringDepositType(final Integer type) { + return recurringDepositType(RecurringDepositType.fromInt(type)); + } + + public static EnumOptionData recurringDepositType(final RecurringDepositType type) { + EnumOptionData optionData = new EnumOptionData(RecurringDepositType.INVALID.getValue().longValue(), + RecurringDepositType.INVALID.getCode(), "Invalid"); + + switch (type) { + case INVALID: + break; + case VOLUNTARY: + optionData = new EnumOptionData(RecurringDepositType.VOLUNTARY.getValue().longValue(), + RecurringDepositType.VOLUNTARY.getCode(), "Voluntary"); + break; + case MANDATORY: + optionData = new EnumOptionData(RecurringDepositType.MANDATORY.getValue().longValue(), + RecurringDepositType.MANDATORY.getCode(), "Mandatory"); + break; + } + + return optionData; + } + + public static List recurringDepositType(final RecurringDepositType[] types) { + final List optionDatas = new ArrayList(); + for (final RecurringDepositType type : types) { + if (!type.isInvalid()) { + optionDatas.add(recurringDepositType(type)); + } + } + return optionDatas; + } + + public static EnumOptionData recurringDepositFrequencyType(final int id) { + return recurringDepositFrequencyType(SavingsPeriodFrequencyType.fromInt(id)); + } + + public static EnumOptionData recurringDepositFrequencyType(final SavingsPeriodFrequencyType type) { + final String codePrefix = "recurring.deposit."; + EnumOptionData optionData = new EnumOptionData(SavingsPeriodFrequencyType.INVALID.getValue().longValue(), + SavingsPeriodFrequencyType.INVALID.getCode(), "Invalid"); + switch (type) { + case INVALID: + break; + case DAYS: + optionData = new EnumOptionData(SavingsPeriodFrequencyType.DAYS.getValue().longValue(), codePrefix + + SavingsPeriodFrequencyType.DAYS.getCode(), "Days"); + break; + case WEEKS: + optionData = new EnumOptionData(SavingsPeriodFrequencyType.WEEKS.getValue().longValue(), codePrefix + + SavingsPeriodFrequencyType.WEEKS.getCode(), "Weeks"); + break; + case MONTHS: + optionData = new EnumOptionData(SavingsPeriodFrequencyType.MONTHS.getValue().longValue(), codePrefix + + SavingsPeriodFrequencyType.MONTHS.getCode(), "Months"); + break; + case YEARS: + optionData = new EnumOptionData(SavingsPeriodFrequencyType.YEARS.getValue().longValue(), codePrefix + + SavingsPeriodFrequencyType.YEARS.getCode(), "Years"); + break; + } + return optionData; + } + + public static List recurringDepositFrequencyType(final SavingsPeriodFrequencyType[] types) { + final List optionDatas = new ArrayList(); + for (final SavingsPeriodFrequencyType type : types) { + if (!type.isInvalid()) { + optionDatas.add(recurringDepositFrequencyType(type)); + } + } + return optionDatas; + } + + public static EnumOptionData depositTermFrequencyType(final int id) { + return depositTermFrequencyType(SavingsPeriodFrequencyType.fromInt(id)); + } + + public static EnumOptionData depositTermFrequencyType(final SavingsPeriodFrequencyType type) { + final String codePrefix = "deposit.term."; + EnumOptionData optionData = new EnumOptionData(SavingsPeriodFrequencyType.INVALID.getValue().longValue(), + SavingsPeriodFrequencyType.INVALID.getCode(), "Invalid"); + switch (type) { + case INVALID: + break; + case DAYS: + optionData = new EnumOptionData(SavingsPeriodFrequencyType.DAYS.getValue().longValue(), codePrefix + + SavingsPeriodFrequencyType.DAYS.getCode(), "Days"); + break; + case WEEKS: + optionData = new EnumOptionData(SavingsPeriodFrequencyType.WEEKS.getValue().longValue(), codePrefix + + SavingsPeriodFrequencyType.WEEKS.getCode(), "Weeks"); + break; + case MONTHS: + optionData = new EnumOptionData(SavingsPeriodFrequencyType.MONTHS.getValue().longValue(), codePrefix + + SavingsPeriodFrequencyType.MONTHS.getCode(), "Months"); + break; + case YEARS: + optionData = new EnumOptionData(SavingsPeriodFrequencyType.YEARS.getValue().longValue(), codePrefix + + SavingsPeriodFrequencyType.YEARS.getCode(), "Years"); + break; + } + return optionData; + } + + public static List depositTermFrequencyType(final SavingsPeriodFrequencyType[] types) { + final List optionDatas = new ArrayList(); + for (final SavingsPeriodFrequencyType type : types) { + if (!type.isInvalid()) { + optionDatas.add(recurringDepositFrequencyType(type)); + } + } + return optionDatas; + } + + public static EnumOptionData inMultiplesOfDepositTermFrequencyType(final int id) { + return depositTermFrequencyType(SavingsPeriodFrequencyType.fromInt(id)); + } + + public static EnumOptionData inMultiplesOfDepositTermFrequencyType(final SavingsPeriodFrequencyType type) { + final String codePrefix = "inmultiples.of.deposit.term."; + EnumOptionData optionData = new EnumOptionData(SavingsPeriodFrequencyType.INVALID.getValue().longValue(), + SavingsPeriodFrequencyType.INVALID.getCode(), "Invalid"); + switch (type) { + case INVALID: + break; + case DAYS: + optionData = new EnumOptionData(SavingsPeriodFrequencyType.DAYS.getValue().longValue(), codePrefix + + SavingsPeriodFrequencyType.DAYS.getCode(), "Days"); + break; + case WEEKS: + optionData = new EnumOptionData(SavingsPeriodFrequencyType.WEEKS.getValue().longValue(), codePrefix + + SavingsPeriodFrequencyType.WEEKS.getCode(), "Weeks"); + break; + case MONTHS: + optionData = new EnumOptionData(SavingsPeriodFrequencyType.MONTHS.getValue().longValue(), codePrefix + + SavingsPeriodFrequencyType.MONTHS.getCode(), "Months"); + break; + case YEARS: + optionData = new EnumOptionData(SavingsPeriodFrequencyType.YEARS.getValue().longValue(), codePrefix + + SavingsPeriodFrequencyType.YEARS.getCode(), "Years"); + break; + } + return optionData; + } + + public static List inMultiplesOfDepositTermFrequencyType(final SavingsPeriodFrequencyType[] types) { + final List optionDatas = new ArrayList(); + for (final SavingsPeriodFrequencyType type : types) { + if (!type.isInvalid()) { + optionDatas.add(recurringDepositFrequencyType(type)); + } + } + return optionDatas; + } + + public static EnumOptionData depositType(final int id) { + return depositType(DepositAccountType.fromInt(id)); + } + + public static EnumOptionData depositType(final DepositAccountType type) { + EnumOptionData optionData = new EnumOptionData(DepositAccountType.INVALID.getValue().longValue(), + DepositAccountType.INVALID.getCode(), "Invalid"); + switch (type) { + case INVALID: + break; + case SAVINGS_DEPOSIT: + optionData = new EnumOptionData(DepositAccountType.SAVINGS_DEPOSIT.getValue().longValue(), DepositAccountType.SAVINGS_DEPOSIT.getCode(), "Savings"); + break; + case FIXED_DEPOSIT: + optionData = new EnumOptionData(DepositAccountType.FIXED_DEPOSIT.getValue().longValue(), DepositAccountType.FIXED_DEPOSIT.getCode(), "Fixed Deposit"); + break; + case RECURRING_DEPOSIT: + optionData = new EnumOptionData(DepositAccountType.RECURRING_DEPOSIT.getValue().longValue(), DepositAccountType.RECURRING_DEPOSIT.getCode(), "Recurring Deposit"); + break; + case CURRENT_DEPOSIT: + optionData = new EnumOptionData(DepositAccountType.CURRENT_DEPOSIT.getValue().longValue(), DepositAccountType.CURRENT_DEPOSIT.getCode(), "Current Deposit"); + break; + } + return optionData; + } + + public static List depositType(final DepositAccountType[] types) { + final List optionDatas = new ArrayList(); + for (final DepositAccountType type : types) { + if (!type.isInvalid()) { + optionDatas.add(depositType(type)); + } + } + return optionDatas; + } + + public static EnumOptionData depositPeriodFrequency(final int id) { + return depositPeriodFrequency(SavingsPeriodFrequencyType.fromInt(id)); + } + + public static EnumOptionData depositPeriodFrequency(final SavingsPeriodFrequencyType type) { + final String codePrefix = "deposit.period."; + EnumOptionData optionData = new EnumOptionData(SavingsPeriodFrequencyType.INVALID.getValue().longValue(), + SavingsPeriodFrequencyType.INVALID.getCode(), "Invalid"); + switch (type) { + case INVALID: + break; + case DAYS: + optionData = new EnumOptionData(SavingsPeriodFrequencyType.DAYS.getValue().longValue(), codePrefix + + SavingsPeriodFrequencyType.DAYS.getCode(), "Days"); + break; + case WEEKS: + optionData = new EnumOptionData(SavingsPeriodFrequencyType.WEEKS.getValue().longValue(), codePrefix + + SavingsPeriodFrequencyType.WEEKS.getCode(), "Weeks"); + break; + case MONTHS: + optionData = new EnumOptionData(SavingsPeriodFrequencyType.MONTHS.getValue().longValue(), codePrefix + + SavingsPeriodFrequencyType.MONTHS.getCode(), "Months"); + break; + case YEARS: + optionData = new EnumOptionData(SavingsPeriodFrequencyType.YEARS.getValue().longValue(), codePrefix + + SavingsPeriodFrequencyType.YEARS.getCode(), "Years"); + break; + } + return optionData; + } + + public static List depositPeriodFrequency(final SavingsPeriodFrequencyType[] types) { + final List optionDatas = new ArrayList(); + for (final SavingsPeriodFrequencyType type : types) { + if (!type.isInvalid()) { + optionDatas.add(recurringDepositFrequencyType(type)); + } + } + return optionDatas; + } + + public static EnumOptionData depositAccountOnClosureType(final int id) { + return depositAccountOnClosureType(DepositAccountOnClosureType.fromInt(id)); + } + + public static EnumOptionData depositAccountOnClosureType(final DepositAccountOnClosureType type) { + EnumOptionData optionData = new EnumOptionData(DepositAccountOnClosureType.INVALID.getValue().longValue(), + DepositAccountOnClosureType.INVALID.getCode(), "Invalid"); + switch (type) { + case INVALID: + break; + case WITHDRAW_DEPOSIT: + optionData = new EnumOptionData(DepositAccountOnClosureType.WITHDRAW_DEPOSIT.getValue().longValue(), DepositAccountOnClosureType.WITHDRAW_DEPOSIT.getCode(), "Withdra Deposit"); + break; + case TRANSFER_TO_SAVINGS: + optionData = new EnumOptionData(DepositAccountOnClosureType.TRANSFER_TO_SAVINGS.getValue().longValue(), DepositAccountOnClosureType.TRANSFER_TO_SAVINGS.getCode(), "Transfer to Savings"); + break; + case REINVEST: + optionData = new EnumOptionData(DepositAccountOnClosureType.REINVEST.getValue().longValue(), DepositAccountOnClosureType.REINVEST.getCode(), "Re-Invest"); + break; + } + return optionData; + } + + public static List depositAccountOnClosureType(final DepositAccountOnClosureType[] types) { + final List optionDatas = new ArrayList(); + for (final DepositAccountOnClosureType type : types) { + if (!type.isInvalid()) { + optionDatas.add(depositAccountOnClosureType(type)); + } + } + return optionDatas; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java index 0fb001aa3..4629eedf4 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java @@ -16,6 +16,7 @@ import org.mifosplatform.infrastructure.core.domain.JdbcSupport; 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.savings.DepositAccountType; import org.mifosplatform.portfolio.savings.data.SavingsProductData; import org.mifosplatform.portfolio.savings.exception.SavingsProductNotFoundException; import org.springframework.beans.factory.annotation.Autowired; @@ -43,25 +44,27 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead this.context.authenticatedUser(); - final String sql = "select " + this.savingsProductRowMapper.schema(); + final String sql = "select " + this.savingsProductRowMapper.schema() + "where sp.deposit_type_enum = ?"; - return this.jdbcTemplate.query(sql, this.savingsProductRowMapper); + return this.jdbcTemplate.query(sql, this.savingsProductRowMapper, new Object[] { DepositAccountType.SAVINGS_DEPOSIT.getValue() }); } @Override public Collection retrieveAllForLookup() { - final String sql = "select " + this.savingsProductLookupsRowMapper.schema(); + final String sql = "select " + this.savingsProductLookupsRowMapper.schema() + " where sp.deposit_type_enum = ?"; - return this.jdbcTemplate.query(sql, this.savingsProductLookupsRowMapper); + return this.jdbcTemplate.query(sql, this.savingsProductLookupsRowMapper, + new Object[] { DepositAccountType.SAVINGS_DEPOSIT.getValue() }); } @Override public SavingsProductData retrieveOne(final Long savingProductId) { try { this.context.authenticatedUser(); - final String sql = "select " + this.savingsProductRowMapper.schema() + " where sp.id = ?"; - return this.jdbcTemplate.queryForObject(sql, this.savingsProductRowMapper, new Object[] { savingProductId }); + final String sql = "select " + this.savingsProductRowMapper.schema() + " where sp.id = ? and sp.deposit_type_enum = ?"; + return this.jdbcTemplate.queryForObject(sql, this.savingsProductRowMapper, new Object[] { savingProductId, + DepositAccountType.SAVINGS_DEPOSIT.getValue() }); } catch (final EmptyResultDataAccessException e) { throw new SavingsProductNotFoundException(savingProductId); } @@ -86,14 +89,9 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead sqlBuilder.append("sp.min_required_opening_balance as minRequiredOpeningBalance, "); sqlBuilder.append("sp.lockin_period_frequency as lockinPeriodFrequency,"); sqlBuilder.append("sp.lockin_period_frequency_enum as lockinPeriodFrequencyType, "); - // sqlBuilder.append("sp.withdrawal_fee_amount as withdrawalFeeAmount,"); - // sqlBuilder.append("sp.withdrawal_fee_type_enum as withdrawalFeeTypeEnum, "); sqlBuilder.append("sp.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, "); sqlBuilder.append("sp.allow_overdraft as allowOverdraft, "); sqlBuilder.append("sp.overdraft_limit as overdraftLimit, "); - // sqlBuilder.append("sp.annual_fee_amount as annualFeeAmount,"); - // sqlBuilder.append("sp.annual_fee_on_month as annualFeeOnMonth, "); - // sqlBuilder.append("sp.annual_fee_on_day as annualFeeOnDay, "); sqlBuilder.append("sp.accounting_type as accountingType "); sqlBuilder.append("from m_savings_product sp "); sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code "); @@ -152,31 +150,9 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(lockinPeriodFrequencyTypeValue); } - /* - * final BigDecimal withdrawalFeeAmount = - * rs.getBigDecimal("withdrawalFeeAmount"); - * - * EnumOptionData withdrawalFeeType = null; final Integer - * withdrawalFeeTypeValue = JdbcSupport.getInteger(rs, - * "withdrawalFeeTypeEnum"); if (withdrawalFeeTypeValue != null) { - * withdrawalFeeType = - * SavingsEnumerations.withdrawalFeeType(withdrawalFeeTypeValue); } - */ final boolean withdrawalFeeForTransfers = rs.getBoolean("withdrawalFeeForTransfers"); final boolean allowOverdraft = rs.getBoolean("allowOverdraft"); final BigDecimal overdraftLimit = rs.getBigDecimal("overdraftLimit"); - /* - * final BigDecimal annualFeeAmount = - * JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, - * "annualFeeAmount"); - * - * MonthDay annualFeeOnMonthDay = null; final Integer - * annualFeeOnMonth = JdbcSupport.getInteger(rs, - * "annualFeeOnMonth"); final Integer annualFeeOnDay = - * JdbcSupport.getInteger(rs, "annualFeeOnDay"); if (annualFeeAmount - * != null && annualFeeOnDay != null) { annualFeeOnMonthDay = new - * MonthDay(annualFeeOnMonth, annualFeeOnDay); } - */ return SavingsProductData.instance(id, name, shortName, description, currency, nominalAnnualInterestRate, compoundingInterestPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, 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 6187fbf86..f9969e132 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 @@ -16,8 +16,10 @@ public interface ScheduledJobRunnerService { void updateLoanPaidInAdvance(); void applyAnnualFeeForSavings(); - + void applyDueChargesForSavings() throws JobExecutionException; void updateNPA(); + + void updateMaturityDetailsOfDepositAccounts(); } 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 136202e5b..beaba35ca 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 @@ -15,7 +15,11 @@ import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil; import org.mifosplatform.infrastructure.jobs.annotation.CronTarget; import org.mifosplatform.infrastructure.jobs.exception.JobExecutionException; import org.mifosplatform.infrastructure.jobs.service.JobName; +import org.mifosplatform.portfolio.savings.DepositAccountType; +import org.mifosplatform.portfolio.savings.data.DepositAccountData; import org.mifosplatform.portfolio.savings.data.SavingsAccountAnnualFeeData; +import org.mifosplatform.portfolio.savings.service.DepositAccountReadPlatformService; +import org.mifosplatform.portfolio.savings.service.DepositAccountWritePlatformService; import org.mifosplatform.portfolio.savings.service.SavingsAccountChargeReadPlatformService; import org.mifosplatform.portfolio.savings.service.SavingsAccountWritePlatformService; import org.slf4j.Logger; @@ -33,14 +37,20 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService private final RoutingDataSourceServiceFactory dataSourceServiceFactory; private final SavingsAccountWritePlatformService savingsAccountWritePlatformService; private final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService; + private final DepositAccountReadPlatformService depositAccountReadPlatformService; + private final DepositAccountWritePlatformService depositAccountWritePlatformService; @Autowired public ScheduledJobRunnerServiceImpl(final RoutingDataSourceServiceFactory dataSourceServiceFactory, final SavingsAccountWritePlatformService savingsAccountWritePlatformService, - final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService) { + final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService, + final DepositAccountReadPlatformService depositAccountReadPlatformService, + final DepositAccountWritePlatformService depositAccountWritePlatformService) { this.dataSourceServiceFactory = dataSourceServiceFactory; this.savingsAccountWritePlatformService = savingsAccountWritePlatformService; this.savingsAccountChargeReadPlatformService = savingsAccountChargeReadPlatformService; + this.depositAccountReadPlatformService = depositAccountReadPlatformService; + this.depositAccountWritePlatformService = depositAccountWritePlatformService; } @Transactional @@ -281,4 +291,28 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Results affected by update: " + result); } + @Override + @CronTarget(jobName = JobName.UPDATE_DEPOSITS_ACCOUNT_MATURITY_DETAILS) + public void updateMaturityDetailsOfDepositAccounts() { + + final Collection depositAccounts = this.depositAccountReadPlatformService.retrieveForMaturityUpdate(); + + for (final DepositAccountData depositAccount : depositAccounts) { + try { + final DepositAccountType depositAccountType = DepositAccountType.fromInt(depositAccount.depositType().getId().intValue()); + this.depositAccountWritePlatformService.updateMaturityDetails(depositAccount.id(), depositAccountType); + } catch (final PlatformApiDataValidationException e) { + final List errors = e.getErrors(); + for (final ApiParameterError error : errors) { + logger.error("Update maturity details failed for account:" + depositAccount.accountNo() + " with message " + + error.getDeveloperMessage()); + } + } catch (final Exception ex) { + // need to handle this scenario + } + } + + logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Deposit accounts affected by update: " + depositAccounts.size()); + } + } \ No newline at end of file