From 209dd4803134d2483a4c326d54cc1cf0be7c5a6b Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Tue, 8 Apr 2014 22:59:16 +0530 Subject: [PATCH] MIFOSX-1055 - added standing instructions --- api-docs/apiLive.htm | 1479 +++++++++++++++++ .../V160__standing_instruction_changes.sql | 126 ++ .../commands/domain/CommandWrapper.java | 6 +- .../service/CommandWrapperBuilder.java | 35 +- .../SynchronousCommandProcessingService.java | 11 + .../core/data/DataValidatorBuilder.java | 19 + .../infrastructure/jobs/service/JobName.java | 3 +- .../account/AccountDetailConstants.java | 27 + .../account/AccountTransfersApiConstants.java | 50 - .../api/AccountTransfersApiConstants.java | 38 + .../api/AccountTransfersApiResource.java | 5 +- .../api/StandingInstructionApiConstants.java | 50 + .../api/StandingInstructionApiResource.java | 195 +++ .../account/data/AccountTransferDTO.java | 17 +- .../data/AccountTransfersDataValidator.java | 49 +- .../AccountTransfersDetailDataValidator.java | 67 + .../account/data/StandingInstructionDTO.java | 49 + .../account/data/StandingInstructionData.java | 376 +++++ .../StandingInstructionDataValidator.java | 237 +++ .../data/StandingInstructionDuesData.java | 25 + .../account/domain/AccountTransfer.java | 182 -- .../domain/AccountTransferAssembler.java | 154 +- .../AccountTransferDetailAssembler.java | 191 +++ .../AccountTransferDetailRepository.java | 12 + .../domain/AccountTransferDetails.java | 153 ++ .../domain/AccountTransferRecurrenceType.java | 59 + .../domain/AccountTransferRepository.java | 14 +- .../AccountTransferStandingInstruction.java | 278 ++++ .../domain/AccountTransferTransaction.java | 140 ++ .../account/domain/AccountTransferType.java | 66 + .../domain/StandingInstructionAssembler.java | 81 + .../domain/StandingInstructionPriority.java | 74 + .../domain/StandingInstructionRepository.java | 12 + .../domain/StandingInstructionStatus.java | 67 + .../domain/StandingInstructionType.java | 59 + .../StandingInstructionNotFoundException.java | 19 + ...eateStandingInstructionCommandHandler.java | 25 + ...leteStandingInstructionCommandHandler.java | 25 + ...dateStandingInstructionCommandHandler.java | 25 + .../service/AccountTransferEnumerations.java | 138 ++ .../AccountTransfersReadPlatformService.java | 4 +- ...countTransfersReadPlatformServiceImpl.java | 84 +- ...ountTransfersWritePlatformServiceImpl.java | 153 +- ...tandingInstructionReadPlatformService.java | 28 + ...ingInstructionReadPlatformServiceImpl.java | 575 +++++++ ...andingInstructionWritePlatformService.java | 22 + ...ngInstructionWritePlatformServiceImpl.java | 245 +++ .../common/domain/PeriodFrequencyType.java | 9 + .../common/service/CommonEnumerations.java | 4 + .../DropdownReadPlatformServiceImpl.java | 10 +- .../domain/LoanAccountDomainServiceJpa.java | 4 +- .../domain/DefaultScheduledDateGenerator.java | 43 + .../domain/ScheduledDateGenerator.java | 2 + .../loanaccount/service/LoanAssembler.java | 5 + .../service/LoanReadPlatformServiceImpl.java | 8 +- ...WritePlatformServiceJpaRepositoryImpl.java | 15 +- .../domain/SavingsAccountAssembler.java | 4 + ...SavingsAccountReadPlatformServiceImpl.java | 4 +- 58 files changed, 5396 insertions(+), 461 deletions(-) create mode 100755 mifosng-db/migrations/core_db/V160__standing_instruction_changes.sql create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/AccountDetailConstants.java delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/AccountTransfersApiConstants.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/api/AccountTransfersApiConstants.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/api/StandingInstructionApiConstants.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/api/StandingInstructionApiResource.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransfersDetailDataValidator.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/StandingInstructionDTO.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/StandingInstructionData.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/StandingInstructionDataValidator.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/StandingInstructionDuesData.java delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransfer.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferDetailAssembler.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferDetailRepository.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferDetails.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferRecurrenceType.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferStandingInstruction.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferTransaction.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferType.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/StandingInstructionAssembler.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/StandingInstructionPriority.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/StandingInstructionRepository.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/StandingInstructionStatus.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/StandingInstructionType.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/exception/StandingInstructionNotFoundException.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/handler/CreateStandingInstructionCommandHandler.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/handler/DeleteStandingInstructionCommandHandler.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/handler/UpdateStandingInstructionCommandHandler.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/StandingInstructionReadPlatformService.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/StandingInstructionReadPlatformServiceImpl.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/StandingInstructionWritePlatformService.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/StandingInstructionWritePlatformServiceImpl.java diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 44de8e9e4..3a6912c8e 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -300,6 +300,54 @@ + + Standing Instructions + standinginstructions + Create Standing Instruction + Retrive a Standing Instruction + Update Standing Instruction + + + + + + + List Standing Instructions + Delete Standing Instruction(status change) + + + + + + + Retrive Standing Instruction Template + + + + + Account Transfer + accounttransfers + Create Account Transfer + Retrive a Account Transfer + + + + + + + + List Account Transfer + + + + + + + + Retrive Account Transfer Template + + + @@ -17422,6 +17470,1437 @@ No Request Body: + + + +  +
+
+

Standing Instruction:

+

Ability to be able to transfer monetary funds from one account to another.

+

Note: At present only savings account to savings account and savings account to Loan account.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Mandatory Parameters
name
Standing Instruction Name.
fromOfficeId
The id of the office/branch from which the transfer is made.
fromClientId
The id of the client from which the transfer is made.
fromAccountType
The type of account from which the transfer is made. 1=Loan Account, 2=Savings Account
fromAccountId
The id of the account from which the transfer is made.
toOfficeId
The id of the office/branch to which the transfer is made.
toClientId
The id of the client to which the transfer is made.
toAccountType
The type of account to which the transfer is made. 1=Loan Account, 2=Savings Account
toAccountId
The id of the account to which the transfer is made. The accouont must be active and must have the same currency as that of the selected fromAccountId.
priority
The priority of instruction while executiing instructions. 1= URGENT,2 = HIGH, 3 = MEDIUM, 4 = LOW
transferType
The type of account transfer from which account to which. 1=Account Transfer(savings to savings), 2=Loan Repayment
instructionType
The Standing instruction amount for transfer type while execution of standing instruction. 1. FIXED, 2.DUES
status
The Standing instruction state. 1. Active, 2.Disabled,3.deleted
recurrenceType
The Standing instruction schedule type for execution.. 1. Periodic, 2.As per dues
validFrom
The Standing instruction's Start date.
+ + + + + + + + + + + + + + + + + + + +
Optional Parameters
amount
Transfer amount while running the instruction. must be provided if instructionType is fixed
validTill
The Standing instruction's Start date.
recurrenceFrequency
The periodic frequency of standing instruction.must be provided if recurrenceType is Periodic. 0=days,1= weeks,2=months,3=years
recurrenceInterval
The periodic frequency interval of standing instruction.must be provided if recurrenceType is Periodic
recurrenceOnMonthDay
The Month and Day of periodic frequency.must be provided if recurrenceFrequency is months or years
+
+
+ +   +
+
+

Retrieve Standing Instruction Template:

+

This is a convenience resource. It can be useful when building maintenance user interface screens for client applications. The template data returned consists of any or all of: +

    +
  • Field Defaults
  • +
  • Allowed Value Lists
  • +
+

+

Example Requests:

+
standinginstructions/template?fromAccountType=2&fromOfficeId=1
+
+
standinginstructions/template?fromAccountType=2&fromOfficeId=1&fromClientId=1&transferType=1
+
+
standinginstructions/template?fromClientId=1&fromAccountType=2&fromAccountId=1&transferType=1
+
+
+
+ GET https://Domain Name/api/v1/standinginstructions/template?fromAccountType=2&fromOfficeId=1 + + + { + "fromOffice": { + "id": 1, + "name": "Head Office", + "nameDecorated": "Head Office", + "externalId": "1", + "openingDate": [ + 2009, + 1, + 1 + ], + "hierarchy": "." + }, + "fromAccountType": { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" + }, + "fromOfficeOptions": [ + { + "id": 1, + "name": "Head Office", + "nameDecorated": "Head Office" + } + ], + "fromClientOptions": [ + { + "id": 1, + "displayName": "Client_FirstName_2VRAG Client_LastName_9QCY", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 2, + "displayName": "Client_FirstName_ZYDN2 Client_LastName_XVOP", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 3, + "displayName": "Client_FirstName_89LYT Client_LastName_4EY6", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 4, + "displayName": "Client_FirstName_PRCBG Client_LastName_JZU2", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 5, + "displayName": "Client_FirstName_J37GR Client_LastName_1T3X", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 6, + "displayName": "Client_FirstName_ZVHM2 Client_LastName_RUGS", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 7, + "displayName": "Client_FirstName_RBALP Client_LastName_437P", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 8, + "displayName": "Client_FirstName_R7M4V Client_LastName_Q5ED", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 9, + "displayName": "Client_FirstName_WIBDE Client_LastName_U91T", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 10, + "displayName": "Client_FirstName_26QJT Client_LastName_BEHD", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 11, + "displayName": "Client_FirstName_W071M Client_LastName_L7Z2", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 12, + "displayName": "Client_FirstName_QUHDJ Client_LastName_S4C5", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 13, + "displayName": "Client_FirstName_MNP4W Client_LastName_J8Y3", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 14, + "displayName": "Client_FirstName_TL6I8 Client_LastName_5YHG", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 15, + "displayName": "Client_FirstName_LUTBO Client_LastName_DITS", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 16, + "displayName": "Client_FirstName_UE39Z Client_LastName_PUWZ", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 17, + "displayName": "Client_FirstName_M8SD2 Client_LastName_J6QK", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 18, + "displayName": "Client_FirstName_SG8NF Client_LastName_BM1J", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 19, + "displayName": "Client_FirstName_BW0C8 Client_LastName_LGV9", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 20, + "displayName": "Client_FirstName_9KYLE Client_LastName_I0GJ", + "officeId": 1, + "officeName": "Head Office" + } + ], + "fromAccountTypeOptions": [ + { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" + }, + { + "id": 1, + "code": "accountType.loan", + "value": "Loan Account" + } + ], + "toOfficeOptions": [ + { + "id": 1, + "name": "Head Office", + "nameDecorated": "Head Office" + } + ], + "toAccountTypeOptions": [ + { + "id": 1, + "code": "accountType.loan", + "value": "Loan Account" + }, + { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" + } + ], + "transferTypeOptions": [ + { + "id": 1, + "code": "accountTransferType.account.transfer", + "value": "Account Transfer" + }, + { + "id": 2, + "code": "accountTransferType.loan.repayment", + "value": "Loan Repayment" + } + ], + "statusOptions": [ + { + "id": 1, + "code": "standingInstructionStatus.active", + "value": "Active" + }, + { + "id": 2, + "code": "standingInstructionStatus.disabled", + "value": "Disabled" + } + ], + "instructionTypeOptions": [ + { + "id": 1, + "code": "standingInstructionType.fixed", + "value": "Fixed" + }, + { + "id": 2, + "code": "standingInstructionType.dues", + "value": "Dues" + } + ], + "priorityOptions": [ + { + "id": 1, + "code": "standingInstructionPriority.urgent", + "value": "Urgent Priority" + }, + { + "id": 2, + "code": "standingInstructionPriority.high", + "value": "High Priority" + }, + { + "id": 3, + "code": "standingInstructionPriority.medium", + "value": "Medium Priority" + }, + { + "id": 4, + "code": "standingInstructionPriority.low", + "value": "Low Priority" + } + ], + "recurrenceTypeOptions": [ + { + "id": 1, + "code": "accountTransferRecurrenceType.periodic", + "value": "Periodic Recurrence" + }, + { + "id": 2, + "code": "accountTransferRecurrenceType.as.per.dues", + "value": "As Per Dues Recurrence" + } + ], + "recurrenceFrequencyOptions": [ + { + "id": 0, + "code": "frequencyperiodFrequencyType.days", + "value": "Days" + }, + { + "id": 1, + "code": "frequencyperiodFrequencyType.weeks", + "value": "Weeks" + }, + { + "id": 2, + "code": "frequencyperiodFrequencyType.months", + "value": "Months" + }, + { + "id": 3, + "code": "frequencyperiodFrequencyType.years", + "value": "Years" + } + ] +} + +
+ +
+ GET https://Domain Name/api/v1/standinginstructions/template?fromAccountType=2&fromOfficeId=1&fromClientId=1&transferType=1 + + + { + "fromOffice": { + "id": 1, + "name": "Head Office", + "nameDecorated": "Head Office", + "externalId": "1", + "openingDate": [ + 2009, + 1, + 1 + ], + "hierarchy": "." + }, + "fromClient": { + "id": 1, + "accountNo": "000000001", + "externalId": "ID_UTMYOEQ", + "status": { + "id": 300, + "code": "clientStatusType.active", + "value": "Active" + }, + "active": true, + "activationDate": [ + 2011, + 1, + 1 + ], + "firstname": "Client_FirstName_2VRAG", + "lastname": "Client_LastName_9QCY", + "displayName": "Client_FirstName_2VRAG Client_LastName_9QCY", + "officeId": 1, + "officeName": "Head Office", + "timeline": { + "submittedOnDate": [ + 2011, + 1, + 1 + ], + "submittedByUsername": "mifos", + "submittedByFirstname": "App", + "submittedByLastname": "Administrator", + "activatedOnDate": [ + 2011, + 1, + 1 + ], + "activatedByUsername": "mifos", + "activatedByFirstname": "App", + "activatedByLastname": "Administrator" + }, + "groups": [] + }, + "fromAccountType": { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" + }, + "fromOfficeOptions": [ + { + "id": 1, + "name": "Head Office", + "nameDecorated": "Head Office" + } + ], + "fromClientOptions": [ + { + "id": 1, + "displayName": "Client_FirstName_2VRAG Client_LastName_9QCY", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 2, + "displayName": "Client_FirstName_ZYDN2 Client_LastName_XVOP", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 3, + "displayName": "Client_FirstName_89LYT Client_LastName_4EY6", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 4, + "displayName": "Client_FirstName_PRCBG Client_LastName_JZU2", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 5, + "displayName": "Client_FirstName_J37GR Client_LastName_1T3X", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 6, + "displayName": "Client_FirstName_ZVHM2 Client_LastName_RUGS", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 7, + "displayName": "Client_FirstName_RBALP Client_LastName_437P", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 8, + "displayName": "Client_FirstName_R7M4V Client_LastName_Q5ED", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 9, + "displayName": "Client_FirstName_WIBDE Client_LastName_U91T", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 10, + "displayName": "Client_FirstName_26QJT Client_LastName_BEHD", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 11, + "displayName": "Client_FirstName_W071M Client_LastName_L7Z2", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 12, + "displayName": "Client_FirstName_QUHDJ Client_LastName_S4C5", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 13, + "displayName": "Client_FirstName_MNP4W Client_LastName_J8Y3", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 14, + "displayName": "Client_FirstName_TL6I8 Client_LastName_5YHG", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 15, + "displayName": "Client_FirstName_LUTBO Client_LastName_DITS", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 16, + "displayName": "Client_FirstName_UE39Z Client_LastName_PUWZ", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 17, + "displayName": "Client_FirstName_M8SD2 Client_LastName_J6QK", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 18, + "displayName": "Client_FirstName_SG8NF Client_LastName_BM1J", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 19, + "displayName": "Client_FirstName_BW0C8 Client_LastName_LGV9", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 20, + "displayName": "Client_FirstName_9KYLE Client_LastName_I0GJ", + "officeId": 1, + "officeName": "Head Office" + } + ], + "fromAccountTypeOptions": [ + { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" + } + ], + "fromAccountOptions": [], + "toOfficeOptions": [ + { + "id": 1, + "name": "Head Office", + "nameDecorated": "Head Office" + } + ], + "toAccountTypeOptions": [ + { + "id": 1, + "code": "accountType.loan", + "value": "Loan Account" + } + ], + "transferTypeOptions": [ + { + "id": 1, + "code": "accountTransferType.account.transfer", + "value": "Account Transfer" + }, + { + "id": 2, + "code": "accountTransferType.loan.repayment", + "value": "Loan Repayment" + } + ], + "statusOptions": [ + { + "id": 1, + "code": "standingInstructionStatus.active", + "value": "Active" + }, + { + "id": 2, + "code": "standingInstructionStatus.disabled", + "value": "Disabled" + } + ], + "instructionTypeOptions": [ + { + "id": 1, + "code": "standingInstructionType.fixed", + "value": "Fixed" + }, + { + "id": 2, + "code": "standingInstructionType.dues", + "value": "Dues" + } + ], + "priorityOptions": [ + { + "id": 1, + "code": "standingInstructionPriority.urgent", + "value": "Urgent Priority" + }, + { + "id": 2, + "code": "standingInstructionPriority.high", + "value": "High Priority" + }, + { + "id": 3, + "code": "standingInstructionPriority.medium", + "value": "Medium Priority" + }, + { + "id": 4, + "code": "standingInstructionPriority.low", + "value": "Low Priority" + } + ], + "recurrenceTypeOptions": [ + { + "id": 1, + "code": "accountTransferRecurrenceType.periodic", + "value": "Periodic Recurrence" + }, + { + "id": 2, + "code": "accountTransferRecurrenceType.as.per.dues", + "value": "As Per Dues Recurrence" + } + ], + "recurrenceFrequencyOptions": [ + { + "id": 0, + "code": "frequencyperiodFrequencyType.days", + "value": "Days" + }, + { + "id": 1, + "code": "frequencyperiodFrequencyType.weeks", + "value": "Weeks" + }, + { + "id": 2, + "code": "frequencyperiodFrequencyType.months", + "value": "Months" + }, + { + "id": 3, + "code": "frequencyperiodFrequencyType.years", + "value": "Years" + } + ] +} + +
+ +
+ GET https://Domain Name/api/v1/standinginstructions/template?fromAccountType=2&fromOfficeId=1&fromClientId=1&transferType=2&fromAccountId=1 + + +{ + "fromOffice": { + "id": 1, + "name": "Head Office", + "nameDecorated": "Head Office", + "externalId": "1", + "openingDate": [ + 2009, + 1, + 1 + ], + "hierarchy": "." + }, + "fromClient": { + "id": 3, + "accountNo": "000000003", + "externalId": "ID_ECEAKAP", + "status": { + "id": 300, + "code": "clientStatusType.active", + "value": "Active" + }, + "active": true, + "activationDate": [ + 2011, + 3, + 4 + ], + "firstname": "Client_FirstName_89LYT", + "lastname": "Client_LastName_4EY6", + "displayName": "Client_FirstName_89LYT Client_LastName_4EY6", + "officeId": 1, + "officeName": "Head Office", + "timeline": { + "submittedOnDate": [ + 2011, + 3, + 4 + ], + "submittedByUsername": "mifos", + "submittedByFirstname": "App", + "submittedByLastname": "Administrator", + "activatedOnDate": [ + 2011, + 3, + 4 + ], + "activatedByUsername": "mifos", + "activatedByFirstname": "App", + "activatedByLastname": "Administrator" + }, + "groups": [] + }, + "fromAccountType": { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" + }, + "fromAccount": { + "id": 1, + "accountNo": "000000001", + "clientId": 3, + "clientName": "Client_FirstName_89LYT Client_LastName_4EY6", + "productId": 1, + "productName": "SAVINGS_PRODUCT_MVA619", + "fieldOfficerId": 0, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 4, + "inMultiplesOf": 0, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + } + }, + "fromOfficeOptions": [ + { + "id": 1, + "name": "Head Office", + "nameDecorated": "Head Office" + } + ], + "fromClientOptions": [ + { + "id": 1, + "displayName": "Client_FirstName_2VRAG Client_LastName_9QCY", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 2, + "displayName": "Client_FirstName_ZYDN2 Client_LastName_XVOP", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 3, + "displayName": "Client_FirstName_89LYT Client_LastName_4EY6", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 4, + "displayName": "Client_FirstName_PRCBG Client_LastName_JZU2", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 5, + "displayName": "Client_FirstName_J37GR Client_LastName_1T3X", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 6, + "displayName": "Client_FirstName_ZVHM2 Client_LastName_RUGS", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 7, + "displayName": "Client_FirstName_RBALP Client_LastName_437P", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 8, + "displayName": "Client_FirstName_R7M4V Client_LastName_Q5ED", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 9, + "displayName": "Client_FirstName_WIBDE Client_LastName_U91T", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 10, + "displayName": "Client_FirstName_26QJT Client_LastName_BEHD", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 11, + "displayName": "Client_FirstName_W071M Client_LastName_L7Z2", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 12, + "displayName": "Client_FirstName_QUHDJ Client_LastName_S4C5", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 13, + "displayName": "Client_FirstName_MNP4W Client_LastName_J8Y3", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 14, + "displayName": "Client_FirstName_TL6I8 Client_LastName_5YHG", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 15, + "displayName": "Client_FirstName_LUTBO Client_LastName_DITS", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 16, + "displayName": "Client_FirstName_UE39Z Client_LastName_PUWZ", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 17, + "displayName": "Client_FirstName_M8SD2 Client_LastName_J6QK", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 18, + "displayName": "Client_FirstName_SG8NF Client_LastName_BM1J", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 19, + "displayName": "Client_FirstName_BW0C8 Client_LastName_LGV9", + "officeId": 1, + "officeName": "Head Office" + }, + { + "id": 20, + "displayName": "Client_FirstName_9KYLE Client_LastName_I0GJ", + "officeId": 1, + "officeName": "Head Office" + } + ], + "fromAccountTypeOptions": [ + { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" + } + ], + "fromAccountOptions": [ + { + "id": 1, + "accountNo": "000000001", + "clientId": 3, + "clientName": "Client_FirstName_89LYT Client_LastName_4EY6", + "productId": 1, + "productName": "SAVINGS_PRODUCT_MVA619", + "fieldOfficerId": 0, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 4, + "inMultiplesOf": 0, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + } + } + ], + "toOfficeOptions": [ + { + "id": 1, + "name": "Head Office", + "nameDecorated": "Head Office" + } + ], + "toAccountTypeOptions": [ + { + "id": 1, + "code": "accountType.loan", + "value": "Loan Account" + } + ], + "transferTypeOptions": [ + { + "id": 1, + "code": "accountTransferType.account.transfer", + "value": "Account Transfer" + }, + { + "id": 2, + "code": "accountTransferType.loan.repayment", + "value": "Loan Repayment" + } + ], + "statusOptions": [ + { + "id": 1, + "code": "standingInstructionStatus.active", + "value": "Active" + }, + { + "id": 2, + "code": "standingInstructionStatus.disabled", + "value": "Disabled" + } + ], + "instructionTypeOptions": [ + { + "id": 1, + "code": "standingInstructionType.fixed", + "value": "Fixed" + }, + { + "id": 2, + "code": "standingInstructionType.dues", + "value": "Dues" + } + ], + "priorityOptions": [ + { + "id": 1, + "code": "standingInstructionPriority.urgent", + "value": "Urgent Priority" + }, + { + "id": 2, + "code": "standingInstructionPriority.high", + "value": "High Priority" + }, + { + "id": 3, + "code": "standingInstructionPriority.medium", + "value": "Medium Priority" + }, + { + "id": 4, + "code": "standingInstructionPriority.low", + "value": "Low Priority" + } + ], + "recurrenceTypeOptions": [ + { + "id": 1, + "code": "accountTransferRecurrenceType.periodic", + "value": "Periodic Recurrence" + }, + { + "id": 2, + "code": "accountTransferRecurrenceType.as.per.dues", + "value": "As Per Dues Recurrence" + } + ], + "recurrenceFrequencyOptions": [ + { + "id": 0, + "code": "frequencyperiodFrequencyType.days", + "value": "Days" + }, + { + "id": 1, + "code": "frequencyperiodFrequencyType.weeks", + "value": "Weeks" + }, + { + "id": 2, + "code": "frequencyperiodFrequencyType.months", + "value": "Months" + }, + { + "id": 3, + "code": "frequencyperiodFrequencyType.years", + "value": "Years" + } + ] +} + +
+ +
+ +   +
+
+

Create new Standing Instruction

+

Ability to create new instruction for transfer of monetary funds from one account to another.

+
+
+ POST https://Domain Name/api/v1/standinginstructions + POST standinginstructions/ +Content-Type: application/json +No Request Body: +{ + "fromOfficeId":1, + "fromClientId":1, + "fromAccountType":2, + "name":"standing instruction", + "transferType":1, + "priority":2, + "status":1, + "fromAccountId":1, + "toOfficeId":1, + "toClientId":1, + "toAccountType":2, + "toAccountId":3, + "instructionType":1, + "amount":"221", + "validFrom":"08 April 2014", + "recurrenceType":1, + "recurrenceInterval":"1", + "recurrenceFrequency":2, + "locale":"en", + "dateFormat":"dd MMMM yyyy", + "recurrenceOnMonthDay":"02 April", + "monthDayFormat":"dd MMMM" +} + + +{ + +"clientId":1, +"resourceId":65 +} + +
+
+ +   +
+
+

Update Standing Instruction

+

Ability to modify existing instruction for transfer of monetary funds from one account to another.

+
+
+ PUT https://Domain Name/api/v1/standinginstructions/1?command=update + PUT standinginstructions/1?command=update +Content-Type: application/json +No Request Body: +{ + "recurrenceInterval":"2" +} + + +{ + "resourceId":20, + "changes":{ + "recurrenceInterval":2 + } +} + +
+
+ +   +
+
+

Delete Standing Instruction

+

Ability to modify existing instruction for transfer of monetary funds from one account to another.

+
+
+ PUT https://Domain Name/api/v1/standinginstructions/1?command=delete + PUT standinginstructions/1?command=delete +Content-Type: application/json +No Request Body: +{ + +} + + +{ + "resourceId":20, + "changes":{ + "status":3 + } +} + +
+
+ +   +
+
+

List Standing Instructions

+

Example Requests:

+
standinginstructions
+
+
+
+ GET https://Domain Name/api/v1/standinginstructions + +{ + "totalFilteredRecords": 2, + "pageItems": [ + { + "id": 1, + "accountDetailId": 6, + "name": "test standing", + "fromOffice": { + "id": 1, + "name": "Head Office" + }, + "fromClient": { + "id": 1, + "displayName": "Test test", + "officeId": 1, + "officeName": "Head Office" + }, + "fromAccountType": { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" + }, + "fromAccount": { + "id": 14, + "accountNo": "000000014", + "productId": 1, + "productName": "savings old" + }, + "toOffice": { + "id": 1, + "name": "Head Office" + }, + "toClient": { + "id": 1, + "displayName": "Test test", + "officeId": 1, + "officeName": "Head Office" + }, + "toAccountType": { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" + }, + "toAccount": { + "id": 3, + "accountNo": "000000003", + "productId": 4, + "productName": "account overdraft" + }, + "transferType": { + "id": 1, + "code": "accountTransferType.account.transfer", + "value": "Account Transfer" + }, + "priority": { + "id": 3, + "code": "standingInstructionPriority.medium", + "value": "Medium Priority" + }, + "instructionType": { + "id": 1, + "code": "standingInstructionType.fixed", + "value": "Fixed" + }, + "status": { + "id": 3, + "code": "standingInstructionStatus.deleted", + "value": "Deleted" + }, + "amount": 150.000000, + "validFrom": [ + 2014, + 4, + 3 + ], + "recurrenceType": { + "id": 1, + "code": "accountTransferRecurrenceType.periodic", + "value": "Periodic Recurrence" + }, + "recurrenceFrequency": { + "id": 2, + "code": "recurrenceperiodFrequencyType.months", + "value": "Months" + }, + "recurrenceInterval": 1, + "recurrenceOnMonthDay": [ + 4, + 3 + ] + }, + { + "id": 2, + "accountDetailId": 7, + "name": "test standing 2", + "fromOffice": { + "id": 1, + "name": "Head Office" + }, + "fromClient": { + "id": 1, + "displayName": "Test test", + "officeId": 1, + "officeName": "Head Office" + }, + "fromAccountType": { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" + }, + "fromAccount": { + "id": 14, + "accountNo": "000000014", + "productId": 1, + "productName": "savings old" + }, + "toOffice": { + "id": 1, + "name": "Head Office" + }, + "toClient": { + "id": 1, + "displayName": "Test test", + "officeId": 1, + "officeName": "Head Office" + }, + "toAccountType": { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" + }, + "toAccount": { + "id": 3, + "accountNo": "000000003", + "productId": 4, + "productName": "account overdraft" + }, + "transferType": { + "id": 1, + "code": "accountTransferType.account.transfer", + "value": "Account Transfer" + }, + "priority": { + "id": 2, + "code": "standingInstructionPriority.high", + "value": "High Priority" + }, + "instructionType": { + "id": 1, + "code": "standingInstructionType.fixed", + "value": "Fixed" + }, + "status": { + "id": 3, + "code": "standingInstructionStatus.deleted", + "value": "Deleted" + }, + "amount": 100.000000, + "validFrom": [ + 2014, + 4, + 3 + ], + "recurrenceType": { + "id": 1, + "code": "accountTransferRecurrenceType.periodic", + "value": "Periodic Recurrence" + }, + "recurrenceFrequency": { + "id": 2, + "code": "recurrenceperiodFrequencyType.months", + "value": "Months" + }, + "recurrenceInterval": 1, + "recurrenceOnMonthDay": [ + 2, + 1 + ] + } + ] +} +
+
+ +   +
+
+

Retrieve Standing Instruction:

+

Example Requests :

+
standinginstructions/1
+
+
+
+ GET https://DomainName/api/v1/standinginstructions/{standingInstructionId} + +{ + "id": 1, + "accountDetailId": 6, + "name": "test standing", + "fromOffice": { + "id": 1, + "name": "Head Office" + }, + "fromClient": { + "id": 1, + "displayName": "Test test", + "officeId": 1, + "officeName": "Head Office" + }, + "fromAccountType": { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" + }, + "fromAccount": { + "id": 14, + "accountNo": "000000014", + "productId": 1, + "productName": "savings old" + }, + "toOffice": { + "id": 1, + "name": "Head Office" + }, + "toClient": { + "id": 1, + "displayName": "Test test", + "officeId": 1, + "officeName": "Head Office" + }, + "toAccountType": { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" + }, + "toAccount": { + "id": 3, + "accountNo": "000000003", + "productId": 4, + "productName": "account overdraft" + }, + "transferType": { + "id": 1, + "code": "accountTransferType.account.transfer", + "value": "Account Transfer" + }, + "priority": { + "id": 3, + "code": "standingInstructionPriority.medium", + "value": "Medium Priority" + }, + "instructionType": { + "id": 1, + "code": "standingInstructionType.fixed", + "value": "Fixed" + }, + "status": { + "id": 3, + "code": "standingInstructionStatus.deleted", + "value": "Deleted" + }, + "amount": 150.000000, + "validFrom": [ + 2014, + 4, + 3 + ], + "recurrenceType": { + "id": 1, + "code": "accountTransferRecurrenceType.periodic", + "value": "Periodic Recurrence" + }, + "recurrenceFrequency": { + "id": 2, + "code": "recurrenceperiodFrequencyType.months", + "value": "Months" + }, + "recurrenceInterval": 1, + "recurrenceOnMonthDay": [ + 4, + 3 + ] +} + +
+
+ +   diff --git a/mifosng-db/migrations/core_db/V160__standing_instruction_changes.sql b/mifosng-db/migrations/core_db/V160__standing_instruction_changes.sql new file mode 100755 index 000000000..4fa97b1fa --- /dev/null +++ b/mifosng-db/migrations/core_db/V160__standing_instruction_changes.sql @@ -0,0 +1,126 @@ +CREATE TABLE `m_account_transfer_details` ( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `from_office_id` BIGINT(20) NOT NULL, + `to_office_id` BIGINT(20) NOT NULL, + `from_client_id` BIGINT(20) NULL DEFAULT NULL, + `to_client_id` BIGINT(20) NULL DEFAULT NULL, + `from_savings_account_id` BIGINT(20) NULL DEFAULT NULL, + `to_savings_account_id` BIGINT(20) NULL DEFAULT NULL, + `from_loan_account_id` BIGINT(20) NULL DEFAULT NULL, + `to_loan_account_id` BIGINT(20) NULL DEFAULT NULL, + `transfer_type` SMALLINT(2) NULL DEFAULT NULL, + `from_savings_transaction_id` BIGINT(20) NULL DEFAULT NULL, + `from_loan_transaction_id` BIGINT(20) NULL DEFAULT NULL, + `to_savings_transaction_id` BIGINT(20) NULL DEFAULT NULL, + `to_loan_transaction_id` BIGINT(20) NULL DEFAULT NULL, + `is_reversed` TINYINT(1) NOT NULL, + `transaction_date` DATE NOT NULL, + `currency_code` VARCHAR(3) NOT NULL, + `currency_digits` SMALLINT(5) NOT NULL, + `currency_multiplesof` SMALLINT(5) NULL DEFAULT NULL, + `amount` DECIMAL(19,6) NOT NULL, + `description` VARCHAR(200) NOT NULL, + PRIMARY KEY (`id`), + INDEX `FK_m_account_transfer_details_from_office` (`from_office_id`), + INDEX `FK_m_account_transfer_details_to_office` (`to_office_id`), + INDEX `FK_m_account_transfer_details_from_client` (`from_client_id`), + INDEX `FK_m_account_transfer_details_to_client` (`to_client_id`), + INDEX `FK_m_account_transfer_details_from_savings_account` (`from_savings_account_id`), + INDEX `FK_m_account_transfer_details_to_savings_account` (`to_savings_account_id`), + INDEX `FK_m_account_transfer_details_from_loan_account` (`from_loan_account_id`), + INDEX `FK_m_account_transfer_details_to_loan_account` (`to_loan_account_id`), + CONSTRAINT `FK_m_account_transfer_details_from_client` FOREIGN KEY (`from_client_id`) REFERENCES `m_client` (`id`), + CONSTRAINT `FK_m_account_transfer_details_from_loan_account` FOREIGN KEY (`from_loan_account_id`) REFERENCES `m_loan` (`id`), + CONSTRAINT `FK_m_account_transfer_details_from_office` FOREIGN KEY (`from_office_id`) REFERENCES `m_office` (`id`), + CONSTRAINT `FK_m_account_transfer_details_from_savings_account` FOREIGN KEY (`from_savings_account_id`) REFERENCES `m_savings_account` (`id`), + CONSTRAINT `FK_m_account_transfer_details_to_client` FOREIGN KEY (`to_client_id`) REFERENCES `m_client` (`id`), + CONSTRAINT `FK_m_account_transfer_details_to_loan_account` FOREIGN KEY (`to_loan_account_id`) REFERENCES `m_loan` (`id`), + CONSTRAINT `FK_m_account_transfer_details_to_office` FOREIGN KEY (`to_office_id`) REFERENCES `m_office` (`id`), + CONSTRAINT `FK_m_account_transfer_details_to_savings_account` FOREIGN KEY (`to_savings_account_id`) REFERENCES `m_savings_account` (`id`) +) +COLLATE='utf8_general_ci' +ENGINE=InnoDB; + +CREATE TABLE `m_account_transfer_standing_instructions` ( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `name` VARCHAR(250) NOT NULL, + `account_transfer_details_id` BIGINT(20) NOT NULL, + `priority` TINYINT(2) NOT NULL, + `status` TINYINT(2) NOT NULL, + `instruction_type` TINYINT(2) NOT NULL, + `amount` DECIMAL(19,6) NULL DEFAULT NULL, + `valid_from` DATE NOT NULL, + `valid_till` DATE NULL DEFAULT NULL, + `recurrence_type` TINYINT(1) NOT NULL, + `recurrence_frequency` SMALLINT(5) NULL DEFAULT NULL, + `recurrence_interval` SMALLINT(5) NULL DEFAULT NULL, + `recurrence_on_day` SMALLINT(2) NULL DEFAULT NULL, + `recurrence_on_month` SMALLINT(2) NULL DEFAULT NULL, + `last_run_date` DATE NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE INDEX `name` (`name`), + INDEX `FK_m_standing_instructions_account_transfer_details` (`account_transfer_details_id`), + CONSTRAINT `FK_m_standing_instructions_account_transfer_details` FOREIGN KEY (`account_transfer_details_id`) REFERENCES `m_account_transfer_details` (`id`) +) +COLLATE='utf8_general_ci' +ENGINE=InnoDB; + + +CREATE TABLE `m_account_transfer_transaction` ( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `account_transfer_details_id` BIGINT(20) NOT NULL, + `from_savings_transaction_id` BIGINT(20) NULL DEFAULT NULL, + `from_loan_transaction_id` BIGINT(20) NULL DEFAULT NULL, + `to_savings_transaction_id` BIGINT(20) NULL DEFAULT NULL, + `to_loan_transaction_id` BIGINT(20) NULL DEFAULT NULL, + `is_reversed` TINYINT(1) NOT NULL, + `transaction_date` DATE NOT NULL, + `currency_code` VARCHAR(3) NOT NULL, + `currency_digits` SMALLINT(5) NOT NULL, + `currency_multiplesof` SMALLINT(5) NULL DEFAULT NULL, + `amount` DECIMAL(19,6) NOT NULL, + `description` VARCHAR(200) NOT NULL, + PRIMARY KEY (`id`), + INDEX `FK_m_account_transfer_transaction_from_m_savings_transaction` (`from_savings_transaction_id`), + INDEX `FK_m_account_transfer_transaction_to_m_savings_transaction` (`to_savings_transaction_id`), + INDEX `FK_m_account_transfer_transaction_to_m_loan_transaction` (`to_loan_transaction_id`), + INDEX `FK_m_account_transfer_transaction_from_m_loan_transaction` (`from_loan_transaction_id`), + INDEX `FK_m_account_transfer_transaction_account_detail` (`account_transfer_details_id`), + CONSTRAINT `FK_m_account_transfer_transaction_account_detail` FOREIGN KEY (`account_transfer_details_id`) REFERENCES `m_account_transfer_details` (`id`), + CONSTRAINT `FK_m_account_transfer_transaction_from_m_loan_transaction` FOREIGN KEY (`from_loan_transaction_id`) REFERENCES `m_loan_transaction` (`id`), + CONSTRAINT `FK_m_account_transfer_transaction_from_m_savings_transaction` FOREIGN KEY (`from_savings_transaction_id`) REFERENCES `m_savings_account_transaction` (`id`), + CONSTRAINT `FK_m_account_transfer_transaction_to_m_loan_transaction` FOREIGN KEY (`to_loan_transaction_id`) REFERENCES `m_loan_transaction` (`id`), + CONSTRAINT `FK_m_account_transfer_transaction_to_m_savings_transaction` FOREIGN KEY (`to_savings_transaction_id`) REFERENCES `m_savings_account_transaction` (`id`) +) +COLLATE='utf8_general_ci' +ENGINE=InnoDB; + + + +INSERT INTO `m_account_transfer_details` (`from_office_id`, `to_office_id`, `from_client_id`, `to_client_id`, `from_savings_account_id`, `to_savings_account_id`, `from_loan_account_id`, `to_loan_account_id`, `from_savings_transaction_id`, `from_loan_transaction_id`, `to_savings_transaction_id`, `to_loan_transaction_id`, `is_reversed`, `transaction_date`, `currency_code`, `currency_digits`, `currency_multiplesof`, `amount`, `description`) select at.from_office_id,at.to_office_id,at.from_client_id,at.to_client_id,at.from_savings_account_id,at.to_savings_account_id,at.from_loan_account_id,at.to_loan_account_id,at.from_savings_transaction_id,at.from_loan_transaction_id,at.to_savings_transaction_id,at.to_loan_transaction_id,at.is_reversed,at.transaction_date,at.currency_code,at.currency_digits,at.currency_multiplesof,at.amount,at.description from m_savings_account_transfer at; + +INSERT INTO `m_account_transfer_transaction` (`account_transfer_details_id`, `from_savings_transaction_id`, `from_loan_transaction_id`, `to_savings_transaction_id`, `to_loan_transaction_id`, `is_reversed`, `transaction_date`, `currency_code`, `currency_digits`, `currency_multiplesof`, `amount`, `description`) select ad.id,ad.from_savings_transaction_id,ad.from_loan_transaction_id,ad.to_savings_transaction_id,ad.to_loan_transaction_id,ad.is_reversed,ad.transaction_date,ad.currency_code,ad.currency_digits,ad.currency_multiplesof,ad.amount,ad.description from m_account_transfer_details ad; + +ALTER TABLE `m_account_transfer_details` + DROP COLUMN `from_savings_transaction_id`, + DROP COLUMN `from_loan_transaction_id`, + DROP COLUMN `to_savings_transaction_id`, + DROP COLUMN `to_loan_transaction_id`, + DROP COLUMN `is_reversed`, + DROP COLUMN `transaction_date`, + DROP COLUMN `currency_code`, + DROP COLUMN `currency_digits`, + DROP COLUMN `currency_multiplesof`, + DROP COLUMN `amount`, + DROP COLUMN `description`; + +DROP TABLE `m_savings_account_transfer`; + +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('account_transfer', 'READ_STANDINGINSTRUCTION ', 'STANDINGINSTRUCTION ', 'READ', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('account_transfer', 'CREATE_STANDINGINSTRUCTION ', 'STANDINGINSTRUCTION ', 'CREATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('account_transfer', 'UPDATE_STANDINGINSTRUCTION ', 'STANDINGINSTRUCTION ', 'UPDATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('account_transfer', 'DELETE_STANDINGINSTRUCTION ', 'STANDINGINSTRUCTION ', 'DELETE', 0); + + +INSERT INTO `job` (`name`, `display_name`, `cron_expression`, `create_time`, `task_priority`, `group_name`, `previous_run_start_time`, `next_run_time`, `job_key`, `initializing_errorlog`, `is_active`, `currently_running`, `updates_allowed`, `scheduler_group`, `is_misfired`) VALUES ('Execute Standing Instruction', 'Execute Standing Instruction', '0 0 0 1/1 * ? *', now(), 5, NULL, NULL, NULL, NULL, NULL, 1, 0, 1, 0, 0); + 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 94df268d7..65dbe71d7 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 @@ -341,7 +341,7 @@ public class CommandWrapper { public boolean isTemplateRessource() { return this.entityName.equalsIgnoreCase("Template"); } - + public boolean isApproveLoanApplication() { return this.actionName.equalsIgnoreCase("APPROVE") && this.entityName.equalsIgnoreCase("LOAN"); } @@ -390,6 +390,10 @@ public class CommandWrapper { return this.actionName.equalsIgnoreCase("UPDATE") && this.entityName.equalsIgnoreCase("DISBURSEMENTDETAIL"); } + public boolean isStandingInstruction() { + return this.entityName.equalsIgnoreCase("STANDINGINSTRUCTION"); + } + public boolean isCloseLoanAsObligationsMet() { return this.actionName.equalsIgnoreCase("CLOSE") && this.entityName.equalsIgnoreCase("LOAN"); } 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 01411be1b..ce450d99a 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 @@ -32,7 +32,7 @@ public class CommandWrapperBuilder { this.loanId = withLoanId; return this; } - + public CommandWrapperBuilder withSavingsId(final Long withSavingsId) { this.savingsId = withSavingsId; return this; @@ -42,17 +42,17 @@ public class CommandWrapperBuilder { this.clientId = withClientId; return this; } - + public CommandWrapperBuilder withGroupId(final Long withGroupId) { this.groupId = withGroupId; return this; } - + public CommandWrapperBuilder withEntityName(final String withEntityName) { this.entityName = withEntityName; return this; } - + public CommandWrapperBuilder withSubEntityId(final Long withSubEntityId) { this.subentityId = withSubEntityId; return this; @@ -949,6 +949,30 @@ public class CommandWrapperBuilder { return this; } + public CommandWrapperBuilder createStandingInstruction() { + this.actionName = "CREATE"; + this.entityName = "STANDINGINSTRUCTION"; + this.entityId = null; + this.href = "/standinginstructions"; + return this; + } + + public CommandWrapperBuilder updateStandingInstruction(final Long standingInstructionId) { + this.actionName = "UPDATE"; + this.entityName = "STANDINGINSTRUCTION"; + this.entityId = standingInstructionId; + this.href = "/standinginstructions"; + return this; + } + + public CommandWrapperBuilder deleteStandingInstruction(final Long standingInstructionId) { + this.actionName = "DELETE"; + this.entityName = "STANDINGINSTRUCTION"; + this.entityId = standingInstructionId; + this.href = "/standinginstructions"; + return this; + } + public CommandWrapperBuilder savingsAccountDeposit(final Long accountId) { this.actionName = "DEPOSIT"; this.entityName = "SAVINGSACCOUNT"; @@ -1452,7 +1476,7 @@ public class CommandWrapperBuilder { this.href = "/clients/" + clientId + "?command=assignStaff"; return this; } - + public CommandWrapperBuilder updateClientSavingsAccount(final Long clientId) { this.actionName = "UPDATESAVINGSACCOUNT"; this.entityName = "CLIENT"; @@ -1462,7 +1486,6 @@ public class CommandWrapperBuilder { return this; } - public CommandWrapperBuilder createProductMix(final Long productId) { this.actionName = "CREATE"; this.entityName = "PRODUCTMIX"; 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 962ca9682..f70c5f308 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 @@ -638,6 +638,17 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer throw new UnsupportedCommandException(wrapper.commandName()); } + } else if (wrapper.isStandingInstruction()) { + if (wrapper.isCreate()) { + handler = this.applicationContext.getBean("createStandingInstructionCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isUpdate()) { + handler = this.applicationContext.getBean("updateStandingInstructionCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isDelete()) { + handler = this.applicationContext.getBean("deleteStandingInstructionCommandHandler", NewCommandSourceHandler.class); + } else { + throw new UnsupportedCommandException(wrapper.commandName()); + } + } else { throw new UnsupportedCommandException(wrapper.commandName()); 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 1beaded8a..933d05b28 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 @@ -16,6 +16,7 @@ import net.fortuna.ical4j.model.ValidationException; import net.fortuna.ical4j.model.property.RRule; import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; import org.quartz.CronExpression; import org.springframework.util.ObjectUtils; @@ -767,4 +768,22 @@ public class DataValidatorBuilder { return this; } + public DataValidatorBuilder validateDateAfter(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.isAfter(dateVal)) { + final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") + .append(this.parameter).append(".is.less.than.date"); + final StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(this.parameter) + .append(" must be greter 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/jobs/service/JobName.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java index dcebb47de..acd7eb40c 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 @@ -11,7 +11,8 @@ public enum JobName { TRANSFER_FEE_CHARGE_FOR_LOANS("Transfer Fee For Loans From Savings"), // ACCOUNTING_RUNNING_BALANCE_UPDATE("Update Accounting Running Balances"), // PAY_DUE_SAVINGS_CHARGES("Pay Due Savings Charges"), // - APPLY_CHARGE_TO_OVERDUE_LOAN_INSTALLMENT("Apply penalty to overdue loans"); + APPLY_CHARGE_TO_OVERDUE_LOAN_INSTALLMENT("Apply penalty to overdue loans"), + EXECUTE_STANDING_INSTRUCTIONS("Execute Standing Instruction"); private final String name; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/AccountDetailConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/AccountDetailConstants.java new file mode 100755 index 000000000..70459c7a4 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/AccountDetailConstants.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.account; + + +public class AccountDetailConstants { + + // general + public static final String localeParamName = "locale"; + public static final String dateFormatParamName = "dateFormat"; + + // savings product and account parameters + public static final String idParamName = "id"; + public static final String fromOfficeIdParamName = "fromOfficeId"; + public static final String fromClientIdParamName = "fromClientId"; + public static final String fromAccountIdParamName = "fromAccountId"; + public static final String fromAccountTypeParamName = "fromAccountType"; + public static final String toOfficeIdParamName = "toOfficeId"; + public static final String toClientIdParamName = "toClientId"; + public static final String toAccountIdParamName = "toAccountId"; + public static final String toAccountTypeParamName = "toAccountType"; + public static final String transferTypeParamName = "transferType"; + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/AccountTransfersApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/AccountTransfersApiConstants.java deleted file mode 100644 index a178fd708..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/AccountTransfersApiConstants.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * 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.account; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -import org.mifosplatform.portfolio.account.data.AccountTransferData; - -public class AccountTransfersApiConstants { - - public static final String ACCOUNT_TRANSFER_RESOURCE_NAME = "accounttransfer"; - - // general - public static final String localeParamName = "locale"; - public static final String dateFormatParamName = "dateFormat"; - - // savings product and account parameters - public static final String idParamName = "id"; - public static final String fromOfficeIdParamName = "fromOfficeId"; - public static final String fromClientIdParamName = "fromClientId"; - public static final String fromAccountIdParamName = "fromAccountId"; - public static final String fromAccountTypeParamName = "fromAccountType"; - public static final String toOfficeIdParamName = "toOfficeId"; - public static final String toClientIdParamName = "toClientId"; - public static final String toAccountIdParamName = "toAccountId"; - public static final String toAccountTypeParamName = "toAccountType"; - - // transaction parameters - public static final String transferDateParamName = "transferDate"; - public static final String transferAmountParamName = "transferAmount"; - public static final String transferDescriptionParamName = "transferDescription"; - - public static final Set REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, - fromOfficeIdParamName, fromClientIdParamName, fromAccountTypeParamName, fromAccountIdParamName, toOfficeIdParamName, - toClientIdParamName, toAccountTypeParamName, toAccountIdParamName, transferDateParamName, transferAmountParamName, - transferDescriptionParamName)); - - /** - * These parameters will match the class level parameters of - * {@link AccountTransferData}. Where possible, we try to get response - * parameters to match those of request parameters. - */ - public static final Set RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList(idParamName, transferDescriptionParamName, - "currency")); -} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/api/AccountTransfersApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/api/AccountTransfersApiConstants.java new file mode 100644 index 000000000..261e28848 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/api/AccountTransfersApiConstants.java @@ -0,0 +1,38 @@ +/** + * 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.account.api; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.mifosplatform.portfolio.account.AccountDetailConstants; +import org.mifosplatform.portfolio.account.data.AccountTransferData; + +public class AccountTransfersApiConstants { + + public static final String ACCOUNT_TRANSFER_RESOURCE_NAME = "accounttransfer"; + + // transaction parameters + public static final String transferDateParamName = "transferDate"; + public static final String transferAmountParamName = "transferAmount"; + public static final String transferDescriptionParamName = "transferDescription"; + + public static final Set REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(AccountDetailConstants.localeParamName, + AccountDetailConstants.dateFormatParamName, AccountDetailConstants.fromOfficeIdParamName, + AccountDetailConstants.fromClientIdParamName, AccountDetailConstants.fromAccountTypeParamName, + AccountDetailConstants.fromAccountIdParamName, AccountDetailConstants.toOfficeIdParamName, + AccountDetailConstants.toClientIdParamName, AccountDetailConstants.toAccountTypeParamName, + AccountDetailConstants.toAccountIdParamName, transferDateParamName, transferAmountParamName, transferDescriptionParamName)); + + /** + * These parameters will match the class level parameters of + * {@link AccountTransferData}. Where possible, we try to get response + * parameters to match those of request parameters. + */ + public static final Set RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList(AccountDetailConstants.idParamName, + transferDescriptionParamName, "currency")); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/api/AccountTransfersApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/api/AccountTransfersApiResource.java index 2e1d123d3..c4d3d3d95 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/api/AccountTransfersApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/api/AccountTransfersApiResource.java @@ -25,7 +25,6 @@ import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSeriali import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; -import org.mifosplatform.portfolio.account.AccountTransfersApiConstants; import org.mifosplatform.portfolio.account.data.AccountTransferData; import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService; import org.mifosplatform.portfolio.group.service.SearchParameters; @@ -94,14 +93,14 @@ public class AccountTransfersApiResource { public String retrieveAll(@Context final UriInfo uriInfo, @QueryParam("sqlSearch") final String sqlSearch, @QueryParam("externalId") final String externalId, @QueryParam("offset") final Integer offset, @QueryParam("limit") final Integer limit, @QueryParam("orderBy") final String orderBy, - @QueryParam("sortOrder") final String sortOrder) { + @QueryParam("sortOrder") final String sortOrder,@QueryParam("accountDetailId") final Long accountDetailId) { this.context.authenticatedUser().validateHasReadPermission(AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME); final SearchParameters searchParameters = SearchParameters.forAccountTransfer(sqlSearch, externalId, offset, limit, orderBy, sortOrder); - final Page transfers = this.accountTransfersReadPlatformService.retrieveAll(searchParameters); + final Page transfers = this.accountTransfersReadPlatformService.retrieveAll(searchParameters, accountDetailId); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); return this.toApiJsonSerializer.serialize(settings, transfers, AccountTransfersApiConstants.RESPONSE_DATA_PARAMETERS); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/api/StandingInstructionApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/api/StandingInstructionApiConstants.java new file mode 100755 index 000000000..3daf4b35e --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/api/StandingInstructionApiConstants.java @@ -0,0 +1,50 @@ +/** + * 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.account.api; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.mifosplatform.portfolio.account.AccountDetailConstants; + +public class StandingInstructionApiConstants { + + public static final String STANDING_INSTRUCTION_RESOURCE_NAME = "standinginstructions"; + + public static final String nameParamName = "name"; + public static final String priorityParamName = "priority"; + public static final String instructionTypeParamName = "instructionType"; + public static final String statusParamName = "status"; + public static final String amountParamName = "amount"; + public static final String validFromParamName = "validFrom"; + public static final String validTillParamName = "validTill"; + public static final String recurrenceTypeParamName = "recurrenceType"; + public static final String recurrenceFrequencyParamName = "recurrenceFrequency"; + public static final String recurrenceIntervalParamName = "recurrenceInterval"; + public static final String recurrenceOnMonthDayParamName = "recurrenceOnMonthDay"; + public static final String monthDayFormatParamName = "monthDayFormat"; + + public static final Set CREATE_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList( + AccountDetailConstants.localeParamName, AccountDetailConstants.dateFormatParamName, + AccountDetailConstants.fromOfficeIdParamName, AccountDetailConstants.fromClientIdParamName, + AccountDetailConstants.fromAccountTypeParamName, AccountDetailConstants.fromAccountIdParamName, + AccountDetailConstants.toOfficeIdParamName, AccountDetailConstants.toClientIdParamName, + AccountDetailConstants.toAccountTypeParamName, AccountDetailConstants.toAccountIdParamName, + AccountDetailConstants.transferTypeParamName, priorityParamName, instructionTypeParamName, statusParamName, amountParamName, + validFromParamName, validTillParamName, recurrenceTypeParamName, recurrenceFrequencyParamName, recurrenceIntervalParamName, + recurrenceOnMonthDayParamName, nameParamName, monthDayFormatParamName)); + + public static final Set UPDATE_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList( + AccountDetailConstants.localeParamName, AccountDetailConstants.dateFormatParamName, priorityParamName, + instructionTypeParamName, statusParamName, amountParamName, validFromParamName, validTillParamName, recurrenceTypeParamName, + recurrenceFrequencyParamName, recurrenceIntervalParamName, recurrenceOnMonthDayParamName, monthDayFormatParamName)); + + public static final Set RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList(AccountDetailConstants.idParamName, + nameParamName, priorityParamName, instructionTypeParamName, statusParamName, AccountDetailConstants.transferTypeParamName, + validFromParamName, validTillParamName)); + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/api/StandingInstructionApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/api/StandingInstructionApiResource.java new file mode 100755 index 000000000..eddce12f3 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/api/StandingInstructionApiResource.java @@ -0,0 +1,195 @@ +/** + * 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.account.api; + +import java.util.Arrays; +import java.util.Set; + +import javax.ws.rs.Consumes; +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.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.core.service.Page; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.account.data.AccountTransferData; +import org.mifosplatform.portfolio.account.data.StandingInstructionDTO; +import org.mifosplatform.portfolio.account.data.StandingInstructionData; +import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService; +import org.mifosplatform.portfolio.account.service.StandingInstructionReadPlatformService; +import org.mifosplatform.portfolio.group.service.SearchParameters; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Path("/standinginstructions") +@Component +@Scope("singleton") +public class StandingInstructionApiResource { + + private final PlatformSecurityContext context; + private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final ApiRequestParameterHelper apiRequestParameterHelper; + private final StandingInstructionReadPlatformService standingInstructionReadPlatformService; + private final AccountTransfersReadPlatformService accountTransfersReadPlatformService; + + @Autowired + public StandingInstructionApiResource(final PlatformSecurityContext context, + final DefaultToApiJsonSerializer toApiJsonSerializer, + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, + final ApiRequestParameterHelper apiRequestParameterHelper, + final StandingInstructionReadPlatformService standingInstructionReadPlatformService, + final AccountTransfersReadPlatformService accountTransfersReadPlatformService) { + this.context = context; + this.toApiJsonSerializer = toApiJsonSerializer; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + this.apiRequestParameterHelper = apiRequestParameterHelper; + this.standingInstructionReadPlatformService = standingInstructionReadPlatformService; + this.accountTransfersReadPlatformService = accountTransfersReadPlatformService; + } + + @GET + @Path("template") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String template(@QueryParam("fromOfficeId") final Long fromOfficeId, @QueryParam("fromClientId") final Long fromClientId, + @QueryParam("fromAccountId") final Long fromAccountId, @QueryParam("fromAccountType") final Integer fromAccountType, + @QueryParam("toOfficeId") final Long toOfficeId, @QueryParam("toClientId") final Long toClientId, + @QueryParam("toAccountId") final Long toAccountId, @QueryParam("toAccountType") final Integer toAccountType, + @QueryParam("transferType") final Integer transferType, @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); + + final StandingInstructionData standingInstructionData = this.standingInstructionReadPlatformService.retrieveTemplate(fromOfficeId, + fromClientId, fromAccountId, fromAccountType, toOfficeId, toClientId, toAccountId, toAccountType, transferType); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, standingInstructionData, + StandingInstructionApiConstants.RESPONSE_DATA_PARAMETERS); + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String create(final String apiRequestBodyAsJson) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().createStandingInstruction().withJson(apiRequestBodyAsJson) + .build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + @PUT + @Path("{standingInstructionId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String update(@PathParam("standingInstructionId") final Long standingInstructionId, final String apiRequestBodyAsJson, + @QueryParam("command") final String commandParam) { + + CommandWrapper commandRequest = null; + if (is(commandParam, "update")) { + commandRequest = new CommandWrapperBuilder().updateStandingInstruction(standingInstructionId).withJson(apiRequestBodyAsJson) + .build(); + } else if (is(commandParam, "delete")) { + commandRequest = new CommandWrapperBuilder().deleteStandingInstruction(standingInstructionId).withJson(apiRequestBodyAsJson) + .build(); + } + + if (commandRequest == null) { throw new UnrecognizedQueryParamException("command", commandParam); } + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + + } + + private boolean is(final String commandParam, final String commandValue) { + return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue); + } + + @GET + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveAll(@Context final UriInfo uriInfo, @QueryParam("sqlSearch") final String sqlSearch, + @QueryParam("externalId") final String externalId, @QueryParam("offset") final Integer offset, + @QueryParam("limit") final Integer limit, @QueryParam("orderBy") final String orderBy, + @QueryParam("sortOrder") final String sortOrder, @QueryParam("transferType") final Integer transferType, + @QueryParam("clientName") final String clientName, @QueryParam("clientId") final Long clientId, + @QueryParam("fromAccountId") final Long fromAccount, @QueryParam("fromAccountType") final Integer fromAccountType) { + + this.context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); + + final SearchParameters searchParameters = SearchParameters.forAccountTransfer(sqlSearch, externalId, offset, limit, orderBy, + sortOrder); + + StandingInstructionDTO standingInstructionDTO = new StandingInstructionDTO(searchParameters, transferType, clientName, clientId, + fromAccount, fromAccountType); + + final Page transfers = this.standingInstructionReadPlatformService.retrieveAll(standingInstructionDTO); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, transfers, StandingInstructionApiConstants.RESPONSE_DATA_PARAMETERS); + } + + @GET + @Path("{standingInstructionId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveOne(@PathParam("standingInstructionId") final Long standingInstructionId, @Context final UriInfo uriInfo, + @QueryParam("sqlSearch") final String sqlSearch, @QueryParam("externalId") final String externalId, + @QueryParam("offset") final Integer offset, @QueryParam("limit") final Integer limit, + @QueryParam("orderBy") final String orderBy, @QueryParam("sortOrder") final String sortOrder) { + + this.context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); + + StandingInstructionData standingInstructionData = this.standingInstructionReadPlatformService.retrieveOne(standingInstructionId); + final SearchParameters searchParameters = SearchParameters.forAccountTransfer(sqlSearch, externalId, offset, limit, orderBy, + sortOrder); + final Set associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); + Page transfers = null; + if (!associationParameters.isEmpty()) { + if (associationParameters.contains("all")) { + associationParameters.addAll(Arrays.asList("transactions", "template")); + } + if (associationParameters.contains("transactions")) { + transfers = this.accountTransfersReadPlatformService.retrieveByStandingInstruction(standingInstructionId, searchParameters); + standingInstructionData = StandingInstructionData.withTransferData(standingInstructionData, transfers); + } + if (associationParameters.contains("template")) { + final StandingInstructionData templateData = this.standingInstructionReadPlatformService.retrieveTemplate( + standingInstructionData.fromClient().officeId(), standingInstructionData.fromClient().id(), standingInstructionData + .fromAccount().accountId(), standingInstructionData.fromAccountType().getValue(), standingInstructionData + .toClient().officeId(), standingInstructionData.toClient().id(), standingInstructionData.toAccount() + .accountId(), standingInstructionData.toAccountType().getValue(), standingInstructionData.transferType() + .getValue()); + standingInstructionData = StandingInstructionData.withTemplateData(standingInstructionData, templateData); + } + } + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, standingInstructionData, + StandingInstructionApiConstants.RESPONSE_DATA_PARAMETERS); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransferDTO.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransferDTO.java index 230d16a6a..4e9c87237 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransferDTO.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransferDTO.java @@ -6,6 +6,7 @@ import java.util.Locale; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormatter; import org.mifosplatform.portfolio.account.PortfolioAccountType; +import org.mifosplatform.portfolio.account.domain.AccountTransferDetails; import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; public class AccountTransferDTO { @@ -24,12 +25,14 @@ public class AccountTransferDTO { private final Integer toTransferType; private final Long chargeId; private final Integer loanInstallmentNumber; + private final Integer transferType; + private final AccountTransferDetails accountTransferDetails; public AccountTransferDTO(final LocalDate transactionDate, final BigDecimal transactionAmount, final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType, final Long fromAccountId, final Long toAccountId, final String description, final Locale locale, final DateTimeFormatter fmt, final PaymentDetail paymentDetail, final Integer fromTransferType, final Integer toTransferType, final Long chargeId, - Integer loanInstallmentNumber) { + Integer loanInstallmentNumber, Integer transferType, final AccountTransferDetails accountTransferDetails) { this.transactionDate = transactionDate; this.transactionAmount = transactionAmount; this.fromAccountType = fromAccountType; @@ -44,6 +47,8 @@ public class AccountTransferDTO { this.toTransferType = toTransferType; this.chargeId = chargeId; this.loanInstallmentNumber = loanInstallmentNumber; + this.transferType = transferType; + this.accountTransferDetails = accountTransferDetails; } public LocalDate getTransactionDate() { @@ -102,4 +107,14 @@ public class AccountTransferDTO { return this.loanInstallmentNumber; } + + public Integer getTransferType() { + return this.transferType; + } + + + public AccountTransferDetails getAccountTransferDetails() { + return this.accountTransferDetails; + } + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransfersDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransfersDataValidator.java index aec86eae7..c9d3b5406 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransfersDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransfersDataValidator.java @@ -5,19 +5,11 @@ */ package org.mifosplatform.portfolio.account.data; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.REQUEST_DATA_PARAMETERS; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromAccountIdParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromAccountTypeParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromClientIdParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromOfficeIdParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toAccountIdParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toAccountTypeParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toClientIdParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toOfficeIdParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferAmountParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferDateParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferDescriptionParamName; +import static org.mifosplatform.portfolio.account.api.AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME; +import static org.mifosplatform.portfolio.account.api.AccountTransfersApiConstants.REQUEST_DATA_PARAMETERS; +import static org.mifosplatform.portfolio.account.api.AccountTransfersApiConstants.transferAmountParamName; +import static org.mifosplatform.portfolio.account.api.AccountTransfersApiConstants.transferDateParamName; +import static org.mifosplatform.portfolio.account.api.AccountTransfersApiConstants.transferDescriptionParamName; import java.lang.reflect.Type; import java.math.BigDecimal; @@ -43,10 +35,13 @@ import com.google.gson.reflect.TypeToken; public class AccountTransfersDataValidator { private final FromJsonHelper fromApiJsonHelper; + private final AccountTransfersDetailDataValidator accountTransfersDetailDataValidator; @Autowired - public AccountTransfersDataValidator(final FromJsonHelper fromApiJsonHelper) { + public AccountTransfersDataValidator(final FromJsonHelper fromApiJsonHelper, + final AccountTransfersDetailDataValidator accountTransfersDetailDataValidator) { this.fromApiJsonHelper = fromApiJsonHelper; + this.accountTransfersDetailDataValidator = accountTransfersDetailDataValidator; } public void validate(final JsonCommand command) { @@ -64,31 +59,7 @@ public class AccountTransfersDataValidator { final JsonElement element = command.parsedJson(); - final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); - baseDataValidator.reset().parameter(fromOfficeIdParamName).value(fromOfficeId).notNull().integerGreaterThanZero(); - - final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); - baseDataValidator.reset().parameter(fromClientIdParamName).value(fromClientId).notNull().integerGreaterThanZero(); - - final Long fromAccountId = this.fromApiJsonHelper.extractLongNamed(fromAccountIdParamName, element); - baseDataValidator.reset().parameter(fromAccountIdParamName).value(fromAccountId).notNull().integerGreaterThanZero(); - - final Integer fromAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(fromAccountTypeParamName, element); - baseDataValidator.reset().parameter(fromAccountTypeParamName).value(fromAccountType) - .isOneOfTheseValues(Integer.valueOf(1), Integer.valueOf(2)); - - final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); - baseDataValidator.reset().parameter(toOfficeIdParamName).value(toOfficeId).notNull().integerGreaterThanZero(); - - final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); - baseDataValidator.reset().parameter(toClientIdParamName).value(toClientId).notNull().integerGreaterThanZero(); - - final Long toAccountId = this.fromApiJsonHelper.extractLongNamed(toAccountIdParamName, element); - baseDataValidator.reset().parameter(toAccountIdParamName).value(toAccountId).notNull().integerGreaterThanZero(); - - final Integer toAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element); - baseDataValidator.reset().parameter(toAccountTypeParamName).value(toAccountType) - .isOneOfTheseValues(Integer.valueOf(1), Integer.valueOf(2)); + this.accountTransfersDetailDataValidator.validate(command, baseDataValidator); final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed(transferDateParamName, element); baseDataValidator.reset().parameter(transferDateParamName).value(transactionDate).notNull(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransfersDetailDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransfersDetailDataValidator.java new file mode 100755 index 000000000..4024c1c7f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransfersDetailDataValidator.java @@ -0,0 +1,67 @@ +/** + * 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.account.data; + +import static org.mifosplatform.portfolio.account.AccountDetailConstants.fromAccountIdParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.fromAccountTypeParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.fromClientIdParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.fromOfficeIdParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.toAccountIdParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.toAccountTypeParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.toClientIdParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.toOfficeIdParamName; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.google.gson.JsonElement; + +@Component +public class AccountTransfersDetailDataValidator { + + private final FromJsonHelper fromApiJsonHelper; + + @Autowired + public AccountTransfersDetailDataValidator(final FromJsonHelper fromApiJsonHelper) { + this.fromApiJsonHelper = fromApiJsonHelper; + } + + public void validate(final JsonCommand command, final DataValidatorBuilder baseDataValidator) { + + final JsonElement element = command.parsedJson(); + + final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); + baseDataValidator.reset().parameter(fromOfficeIdParamName).value(fromOfficeId).notNull().integerGreaterThanZero(); + + final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); + baseDataValidator.reset().parameter(fromClientIdParamName).value(fromClientId).notNull().integerGreaterThanZero(); + + final Long fromAccountId = this.fromApiJsonHelper.extractLongNamed(fromAccountIdParamName, element); + baseDataValidator.reset().parameter(fromAccountIdParamName).value(fromAccountId).notNull().integerGreaterThanZero(); + + final Integer fromAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(fromAccountTypeParamName, element); + baseDataValidator.reset().parameter(fromAccountTypeParamName).value(fromAccountType).notNull() + .isOneOfTheseValues(Integer.valueOf(1), Integer.valueOf(2)); + + final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); + baseDataValidator.reset().parameter(toOfficeIdParamName).value(toOfficeId).notNull().integerGreaterThanZero(); + + final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); + baseDataValidator.reset().parameter(toClientIdParamName).value(toClientId).notNull().integerGreaterThanZero(); + + final Long toAccountId = this.fromApiJsonHelper.extractLongNamed(toAccountIdParamName, element); + baseDataValidator.reset().parameter(toAccountIdParamName).value(toAccountId).notNull().integerGreaterThanZero(); + + final Integer toAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element); + baseDataValidator.reset().parameter(toAccountTypeParamName).value(toAccountType).notNull() + .isOneOfTheseValues(Integer.valueOf(1), Integer.valueOf(2)); + + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/StandingInstructionDTO.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/StandingInstructionDTO.java new file mode 100755 index 000000000..c4eb19055 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/StandingInstructionDTO.java @@ -0,0 +1,49 @@ +package org.mifosplatform.portfolio.account.data; + +import org.mifosplatform.portfolio.group.service.SearchParameters; + +public class StandingInstructionDTO { + + final SearchParameters searchParameters; + final Long clientId; + final String clientName; + final Integer transferType; + final Integer fromAccountType; + final Long fromAccount; + + public StandingInstructionDTO(final SearchParameters searchParameters, final Integer transferType, final String clientName, + final Long clientId, final Long fromAccount,final Integer fromAccountType) { + this.searchParameters = searchParameters; + this.transferType = transferType; + this.clientName = clientName; + this.clientId = clientId; + this.fromAccount = fromAccount; + this.fromAccountType = fromAccountType; + } + + public SearchParameters searchParameters() { + return this.searchParameters; + } + + public Long clientId() { + return this.clientId; + } + + public String clientName() { + return this.clientName; + } + + public Integer transferType() { + return this.transferType; + } + + public Long fromAccount() { + return this.fromAccount; + } + + + public Integer fromAccountType() { + return this.fromAccountType; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/StandingInstructionData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/StandingInstructionData.java new file mode 100755 index 000000000..a7ed7af5d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/StandingInstructionData.java @@ -0,0 +1,376 @@ +/** + * 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.account.data; + +import java.math.BigDecimal; +import java.util.Collection; + +import org.joda.time.DateTimeFieldType; +import org.joda.time.LocalDate; +import org.joda.time.MonthDay; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.infrastructure.core.service.Page; +import org.mifosplatform.organisation.office.data.OfficeData; +import org.mifosplatform.portfolio.account.PortfolioAccountType; +import org.mifosplatform.portfolio.account.domain.AccountTransferRecurrenceType; +import org.mifosplatform.portfolio.account.domain.AccountTransferType; +import org.mifosplatform.portfolio.account.domain.StandingInstructionType; +import org.mifosplatform.portfolio.client.data.ClientData; +import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType; +import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType; + +/** + * Immutable data object representing a savings account. + */ +@SuppressWarnings("unused") +public class StandingInstructionData { + + private final Long id; + private final Long accountDetailId; + private final String name; + private final OfficeData fromOffice; + private final ClientData fromClient; + private final EnumOptionData fromAccountType; + private final PortfolioAccountData fromAccount; + private final OfficeData toOffice; + private final ClientData toClient; + private final EnumOptionData toAccountType; + private final PortfolioAccountData toAccount; + private final EnumOptionData transferType; + private final EnumOptionData priority; + private final EnumOptionData instructionType; + private final EnumOptionData status; + private final BigDecimal amount; + private final LocalDate validFrom; + private final LocalDate validTill; + private final EnumOptionData recurrenceType; + private final EnumOptionData recurrenceFrequency; + private final Integer recurrenceInterval; + private final MonthDay recurrenceOnMonthDay; + private final Page transactions; + + private final Collection fromOfficeOptions; + private final Collection fromClientOptions; + private final Collection fromAccountTypeOptions; + private final Collection fromAccountOptions; + private final Collection toOfficeOptions; + private final Collection toClientOptions; + private final Collection toAccountTypeOptions; + private final Collection toAccountOptions; + private final Collection transferTypeOptions; + private final Collection statusOptions; + private final Collection instructionTypeOptions; + private final Collection priorityOptions; + private final Collection recurrenceTypeOptions; + private final Collection recurrenceFrequencyOptions; + + public static StandingInstructionData template(final Collection fromOfficeOptions, + final Collection fromClientOptions, final Collection fromAccountTypeOptions, + final Collection fromAccountOptions, final Collection toOfficeOptions, + final Collection toClientOptions, final Collection toAccountTypeOptions, + final Collection toAccountOptions, final Collection transferTypeOptions, + final Collection statusOptions, final Collection instructionTypeOptions, + final Collection priorityOptions, final Collection recurrenceTypeOptions, + final Collection recurrenceFrequencyOptions) { + + final Long id = null; + final Long accountDetailId = null; + final String name = null; + final OfficeData fromOffice = null; + final OfficeData toOffice = null; + final ClientData fromClient = null; + final ClientData toClient = null; + final EnumOptionData fromAccountType = null; + final PortfolioAccountData fromAccount = null; + final EnumOptionData toAccountType = null; + final PortfolioAccountData toAccount = null; + final EnumOptionData transferType = null; + final EnumOptionData priority = null; + final EnumOptionData instructionType = null; + final EnumOptionData status = null; + final BigDecimal amount = null; + final LocalDate validFrom = null; + final LocalDate validTill = null; + final EnumOptionData recurrenceType = null; + final EnumOptionData recurrenceFrequency = null; + final Integer recurrenceInterval = null; + final MonthDay recurrenceOnMonthDay = null; + final Page transactions = null; + + return new StandingInstructionData(id, accountDetailId, name, fromOffice, fromClient, fromAccountType, fromAccount, toOffice, + toClient, toAccountType, toAccount, transferType, priority, instructionType, status, amount, validFrom, validTill, + recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay, transactions, fromOfficeOptions, + fromClientOptions, fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions, + toAccountOptions, transferTypeOptions, statusOptions, instructionTypeOptions, priorityOptions, recurrenceTypeOptions, + recurrenceFrequencyOptions); + } + + public static StandingInstructionData instance(final Long id, final Long accountDetailId, final String name, + final OfficeData fromOffice, final OfficeData toOffice, final ClientData fromClient, final ClientData toClient, + final EnumOptionData fromAccountType, final PortfolioAccountData fromAccount, final EnumOptionData toAccountType, + final PortfolioAccountData toAccount, final EnumOptionData transferType, final EnumOptionData priority, + final EnumOptionData instructionType, final EnumOptionData status, final BigDecimal amount, final LocalDate validFrom, + final LocalDate validTill, final EnumOptionData recurrenceType, final EnumOptionData recurrenceFrequency, + final Integer recurrenceInterval, final MonthDay recurrenceOnMonthDay) { + final Page transactions = null; + final Collection fromOfficeOptions = null; + final Collection fromClientOptions = null; + final Collection fromAccountTypeOptions = null; + final Collection fromAccountOptions = null; + final Collection toOfficeOptions = null; + final Collection toClientOptions = null; + final Collection toAccountTypeOptions = null; + final Collection toAccountOptions = null; + final Collection transferTypeOptions = null; + final Collection statusOptions = null; + final Collection instructionTypeOptions = null; + final Collection priorityOptions = null; + final Collection recurrenceTypeOptions = null; + final Collection recurrenceFrequencyOptions = null; + + return new StandingInstructionData(id, accountDetailId, name, fromOffice, fromClient, fromAccountType, fromAccount, toOffice, + toClient, toAccountType, toAccount, transferType, priority, instructionType, status, amount, validFrom, validTill, + recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay, transactions, fromOfficeOptions, + fromClientOptions, fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions, + toAccountOptions, transferTypeOptions, statusOptions, instructionTypeOptions, priorityOptions, recurrenceTypeOptions, + recurrenceFrequencyOptions); + } + + public static StandingInstructionData withTemplateData(StandingInstructionData instructionData, StandingInstructionData templateData) { + return new StandingInstructionData(instructionData.id, instructionData.accountDetailId, instructionData.name, + instructionData.fromOffice, instructionData.fromClient, instructionData.fromAccountType, instructionData.fromAccount, + instructionData.toOffice, instructionData.toClient, instructionData.toAccountType, instructionData.toAccount, + instructionData.transferType, instructionData.priority, instructionData.instructionType, instructionData.status, + instructionData.amount, instructionData.validFrom, instructionData.validTill, instructionData.recurrenceType, + instructionData.recurrenceFrequency, instructionData.recurrenceInterval, instructionData.recurrenceOnMonthDay, + instructionData.transactions, templateData.fromOfficeOptions, templateData.fromClientOptions, + templateData.fromAccountTypeOptions, templateData.fromAccountOptions, templateData.toOfficeOptions, + templateData.toClientOptions, templateData.toAccountTypeOptions, templateData.toAccountOptions, + templateData.transferTypeOptions, templateData.statusOptions, templateData.instructionTypeOptions, + templateData.priorityOptions, templateData.recurrenceTypeOptions, templateData.recurrenceFrequencyOptions); + } + + private StandingInstructionData(final Long id, final Long accountDetailId, final String name, final OfficeData fromOffice, + final ClientData fromClient, final EnumOptionData fromAccountType, final PortfolioAccountData fromAccount, + final OfficeData toOffice, final ClientData toClient, final EnumOptionData toAccountType, final PortfolioAccountData toAccount, + final EnumOptionData transferType, final EnumOptionData priority, final EnumOptionData instructionType, + final EnumOptionData status, final BigDecimal amount, final LocalDate validFrom, LocalDate validTill, + final EnumOptionData recurrenceType, final EnumOptionData recurrenceFrequency, final Integer recurrenceInterval, + final MonthDay recurrenceOnMonthDay, final Page transactions, + final Collection fromOfficeOptions, final Collection fromClientOptions, + final Collection fromAccountTypeOptions, final Collection fromAccountOptions, + final Collection toOfficeOptions, final Collection toClientOptions, + final Collection toAccountTypeOptions, final Collection toAccountOptions, + final Collection transferTypeOptions, final Collection statusOptions, + final Collection instructionTypeOptions, final Collection priorityOptions, + final Collection recurrenceTypeOptions, final Collection recurrenceFrequencyOptions) { + this.id = id; + this.accountDetailId = accountDetailId; + this.name = name; + this.fromOffice = fromOffice; + this.fromClient = fromClient; + this.fromAccountType = fromAccountType; + this.fromAccount = fromAccount; + this.toOffice = toOffice; + this.toClient = toClient; + this.toAccountType = toAccountType; + this.toAccount = toAccount; + this.transferType = transferType; + + this.priority = priority; + this.instructionType = instructionType; + this.status = status; + this.amount = amount; + this.validFrom = validFrom; + this.validTill = validTill; + this.recurrenceType = recurrenceType; + this.recurrenceFrequency = recurrenceFrequency; + this.recurrenceInterval = recurrenceInterval; + this.recurrenceOnMonthDay = recurrenceOnMonthDay; + + this.fromOfficeOptions = fromOfficeOptions; + this.fromClientOptions = fromClientOptions; + this.fromAccountTypeOptions = fromAccountTypeOptions; + this.fromAccountOptions = fromAccountOptions; + this.toOfficeOptions = toOfficeOptions; + this.toClientOptions = toClientOptions; + this.toAccountTypeOptions = toAccountTypeOptions; + this.toAccountOptions = toAccountOptions; + this.transferTypeOptions = transferTypeOptions; + this.statusOptions = statusOptions; + this.instructionTypeOptions = instructionTypeOptions; + this.priorityOptions = priorityOptions; + this.recurrenceTypeOptions = recurrenceTypeOptions; + this.recurrenceFrequencyOptions = recurrenceFrequencyOptions; + this.transactions = transactions; + } + + public static StandingInstructionData template(OfficeData fromOffice, ClientData fromClient, EnumOptionData fromAccountType, + PortfolioAccountData fromAccount, LocalDate transferDate, OfficeData toOffice, ClientData toClient, + EnumOptionData toAccountType, PortfolioAccountData toAccount, final Collection fromOfficeOptions, + final Collection fromClientOptions, final Collection fromAccountTypeOptions, + final Collection fromAccountOptions, final Collection toOfficeOptions, + final Collection toClientOptions, final Collection toAccountTypeOptions, + final Collection toAccountOptions, final Collection transferTypeOptions, + final Collection statusOptions, final Collection instructionTypeOptions, + final Collection priorityOptions, final Collection recurrenceTypeOptions, + final Collection recurrenceFrequencyOptions) { + final Long id = null; + final Long accountDetailId = null; + final String name = null; + final EnumOptionData transferType = null; + final EnumOptionData priority = null; + final EnumOptionData instructionType = null; + final EnumOptionData status = null; + final BigDecimal amount = null; + final LocalDate validFrom = null; + final LocalDate validTill = null; + final EnumOptionData recurrenceType = null; + final EnumOptionData recurrenceFrequency = null; + final Integer recurrenceInterval = null; + final MonthDay recurrenceOnMonthDay = null; + final Page transactions = null; + + return new StandingInstructionData(id, accountDetailId, name, fromOffice, fromClient, fromAccountType, fromAccount, toOffice, + toClient, toAccountType, toAccount, transferType, priority, instructionType, status, amount, validFrom, validTill, + recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay, transactions, fromOfficeOptions, + fromClientOptions, fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions, + toAccountOptions, transferTypeOptions, statusOptions, instructionTypeOptions, priorityOptions, recurrenceTypeOptions, + recurrenceFrequencyOptions); + } + + public static StandingInstructionData withTransferData(StandingInstructionData instructionData, + final Page transactions) { + return new StandingInstructionData(instructionData.id, instructionData.accountDetailId, instructionData.name, + instructionData.fromOffice, instructionData.fromClient, instructionData.fromAccountType, instructionData.fromAccount, + instructionData.toOffice, instructionData.toClient, instructionData.toAccountType, instructionData.toAccount, + instructionData.transferType, instructionData.priority, instructionData.instructionType, instructionData.status, + instructionData.amount, instructionData.validFrom, instructionData.validTill, instructionData.recurrenceType, + instructionData.recurrenceFrequency, instructionData.recurrenceInterval, instructionData.recurrenceOnMonthDay, + transactions, instructionData.fromOfficeOptions, instructionData.fromClientOptions, instructionData.fromAccountTypeOptions, + instructionData.fromAccountOptions, instructionData.toOfficeOptions, instructionData.toClientOptions, + instructionData.toAccountTypeOptions, instructionData.toAccountOptions, instructionData.transferTypeOptions, + instructionData.statusOptions, instructionData.instructionTypeOptions, instructionData.priorityOptions, + instructionData.recurrenceTypeOptions, instructionData.recurrenceFrequencyOptions); + } + + public StandingInstructionType instructionType() { + StandingInstructionType standingInstructionType = null; + if (this.instructionType != null) { + standingInstructionType = StandingInstructionType.fromInt(this.instructionType.getId().intValue()); + } + return standingInstructionType; + } + + public AccountTransferRecurrenceType recurrenceType() { + AccountTransferRecurrenceType recurrenceType = null; + if (this.recurrenceType != null) { + recurrenceType = AccountTransferRecurrenceType.fromInt(this.recurrenceType.getId().intValue()); + } + return recurrenceType; + } + + public PeriodFrequencyType recurrenceFrequency() { + PeriodFrequencyType frequencyType = null; + if (this.recurrenceFrequency != null) { + frequencyType = PeriodFrequencyType.fromInt(this.recurrenceFrequency.getId().intValue()); + } + return frequencyType; + } + + public PortfolioAccountType fromAccountType() { + PortfolioAccountType accountType = null; + if (this.fromAccountType != null) { + accountType = PortfolioAccountType.fromInt(this.fromAccountType.getId().intValue()); + } + return accountType; + } + + public PortfolioAccountType toAccountType() { + PortfolioAccountType accountType = null; + if (this.toAccountType != null) { + accountType = PortfolioAccountType.fromInt(this.toAccountType.getId().intValue()); + } + return accountType; + } + + public AccountTransferType transferType() { + AccountTransferType accountTransferType = null; + if (this.transferType != null) { + accountTransferType = AccountTransferType.fromInt(this.transferType.getId().intValue()); + } + return accountTransferType; + } + + public Integer recurrenceInterval() { + return this.recurrenceInterval; + } + + public Integer recurrenceOnDay() { + Integer recurrenceOnDay = 0; + if (this.recurrenceOnMonthDay != null) { + recurrenceOnDay = this.recurrenceOnMonthDay.get(DateTimeFieldType.dayOfMonth()); + } + return recurrenceOnDay; + } + + public Integer recurrenceOnMonth() { + Integer recurrenceOnMonth = 0; + if (this.recurrenceOnMonthDay != null) { + recurrenceOnMonth = this.recurrenceOnMonthDay.get(DateTimeFieldType.monthOfYear()); + } + return recurrenceOnMonth; + } + + public LocalDate validFrom() { + return this.validFrom; + } + + public BigDecimal amount() { + return this.amount; + } + + public PortfolioAccountData fromAccount() { + return this.fromAccount; + } + + public PortfolioAccountData toAccount() { + return this.toAccount; + } + + public String name() { + return this.name; + } + + public Integer toTransferType() { + Integer transferType = null; + AccountTransferType accountTransferType = transferType(); + if (accountTransferType.isChargePayment()) { + transferType = LoanTransactionType.CHARGE_PAYMENT.getValue(); + } else if (accountTransferType.isLoanRepayment()) { + transferType = LoanTransactionType.REPAYMENT.getValue(); + } + return transferType; + } + + public Long accountDetailId() { + return this.accountDetailId; + } + + public ClientData fromClient() { + return this.fromClient; + } + + public ClientData toClient() { + return this.toClient; + } + + + public Long getId() { + return this.id; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/StandingInstructionDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/StandingInstructionDataValidator.java new file mode 100755 index 000000000..09986bcbc --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/StandingInstructionDataValidator.java @@ -0,0 +1,237 @@ +/** + * 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.account.data; + +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.CREATE_REQUEST_DATA_PARAMETERS; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.UPDATE_REQUEST_DATA_PARAMETERS; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.amountParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.instructionTypeParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.nameParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.priorityParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.recurrenceFrequencyParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.recurrenceIntervalParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.recurrenceOnMonthDayParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.recurrenceTypeParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.statusParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.validFromParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.validTillParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.fromAccountTypeParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.toAccountTypeParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.transferTypeParamName; + +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.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.account.PortfolioAccountType; +import org.mifosplatform.portfolio.account.domain.AccountTransferRecurrenceType; +import org.mifosplatform.portfolio.account.domain.AccountTransferType; +import org.mifosplatform.portfolio.account.domain.StandingInstructionType; +import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType; +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 StandingInstructionDataValidator { + + private final FromJsonHelper fromApiJsonHelper; + private final AccountTransfersDetailDataValidator accountTransfersDetailDataValidator; + + @Autowired + public StandingInstructionDataValidator(final FromJsonHelper fromApiJsonHelper, + final AccountTransfersDetailDataValidator accountTransfersDetailDataValidator) { + this.fromApiJsonHelper = fromApiJsonHelper; + this.accountTransfersDetailDataValidator = accountTransfersDetailDataValidator; + } + + public void validateForCreate(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, CREATE_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(STANDING_INSTRUCTION_RESOURCE_NAME); + this.accountTransfersDetailDataValidator.validate(command, baseDataValidator); + + final JsonElement element = command.parsedJson(); + + final Integer status = this.fromApiJsonHelper.extractIntegerNamed(statusParamName, element, Locale.getDefault()); + baseDataValidator.reset().parameter(statusParamName).value(status).notNull().inMinMaxRange(1, 2); + + final LocalDate validFrom = this.fromApiJsonHelper.extractLocalDateNamed(validFromParamName, element); + baseDataValidator.reset().parameter(validFromParamName).value(validFrom).notNull(); + + final LocalDate validTill = this.fromApiJsonHelper.extractLocalDateNamed(validTillParamName, element); + baseDataValidator.reset().parameter(validTillParamName).value(validTill).validateDateAfter(validFrom); + + final BigDecimal transferAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(amountParamName, element); + baseDataValidator.reset().parameter(amountParamName).value(transferAmount).positiveAmount(); + + final Integer transferType = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault()); + baseDataValidator.reset().parameter(transferTypeParamName).value(transferType).notNull().inMinMaxRange(1, 3); + + final Integer priority = this.fromApiJsonHelper.extractIntegerNamed(priorityParamName, element, Locale.getDefault()); + baseDataValidator.reset().parameter(priorityParamName).value(priority).notNull().inMinMaxRange(1, 4); + + final Integer standingInstructionType = this.fromApiJsonHelper.extractIntegerNamed(instructionTypeParamName, element, + Locale.getDefault()); + baseDataValidator.reset().parameter(instructionTypeParamName).value(standingInstructionType).notNull().inMinMaxRange(1, 2); + + final Integer recurrenceType = this.fromApiJsonHelper.extractIntegerNamed(recurrenceTypeParamName, element, Locale.getDefault()); + baseDataValidator.reset().parameter(recurrenceTypeParamName).value(recurrenceType).notNull().inMinMaxRange(1, 2); + boolean isPeriodic = false; + if (recurrenceType != null) { + isPeriodic = AccountTransferRecurrenceType.fromInt(recurrenceType).isPeriodicRecurrence(); + } + + final Integer recurrenceFrequency = this.fromApiJsonHelper.extractIntegerNamed(recurrenceFrequencyParamName, element, + Locale.getDefault()); + baseDataValidator.reset().parameter(recurrenceFrequencyParamName).value(recurrenceFrequency).inMinMaxRange(0, 3); + + if (recurrenceFrequency != null) { + PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(recurrenceFrequency); + if (frequencyType.isMonthly() || frequencyType.isYearly()) { + final MonthDay monthDay = this.fromApiJsonHelper.extractMonthDayNamed(recurrenceOnMonthDayParamName, element); + baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(monthDay).notNull(); + } + } + + final Integer recurrenceInterval = this.fromApiJsonHelper.extractIntegerNamed(recurrenceIntervalParamName, element, + Locale.getDefault()); + if (isPeriodic) { + baseDataValidator.reset().parameter(recurrenceIntervalParamName).value(recurrenceInterval).notNull(); + baseDataValidator.reset().parameter(recurrenceFrequencyParamName).value(recurrenceFrequency).notNull(); + } + baseDataValidator.reset().parameter(recurrenceIntervalParamName).value(recurrenceInterval).integerGreaterThanZero(); + + final String name = this.fromApiJsonHelper.extractStringNamed(nameParamName, element); + baseDataValidator.reset().parameter(nameParamName).value(name).notNull(); + + final Integer toAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element); + if (toAccountType != null && PortfolioAccountType.fromInt(toAccountType).isSavingsAccount()) { + baseDataValidator.reset().parameter(instructionTypeParamName).value(standingInstructionType).notNull().inMinMaxRange(1, 1); + baseDataValidator.reset().parameter(recurrenceTypeParamName).value(recurrenceType).notNull().inMinMaxRange(1, 1); + + } + if (standingInstructionType != null && StandingInstructionType.fromInt(standingInstructionType).isFixedAmoutTransfer()) { + baseDataValidator.reset().parameter(amountParamName).value(transferAmount).notNull(); + } + + String errorCode = null; + AccountTransferType accountTransferType = AccountTransferType.fromInt(transferType); + final Integer fromAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(fromAccountTypeParamName, element); + if (fromAccountType != null && toAccountType != null) { + PortfolioAccountType fromPortfolioAccountType = PortfolioAccountType.fromInt(fromAccountType); + PortfolioAccountType toPortfolioAccountType = PortfolioAccountType.fromInt(toAccountType); + if (accountTransferType.isAccountTransfer() + && (fromPortfolioAccountType.isLoanAccount() || toPortfolioAccountType.isLoanAccount())) { + errorCode = "not.account.transfer"; + } else if (accountTransferType.isLoanRepayment() + && (fromPortfolioAccountType.isLoanAccount() || toPortfolioAccountType.isSavingsAccount())) { + errorCode = "not.loan.repayment"; + } + if (errorCode != null) { + baseDataValidator.reset().parameter(transferTypeParamName).failWithCode(errorCode); + } + } + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public void validateForUpdate(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, UPDATE_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(STANDING_INSTRUCTION_RESOURCE_NAME); + + final JsonElement element = command.parsedJson(); + if (this.fromApiJsonHelper.parameterExists(validFromParamName, element)) { + final LocalDate validFrom = this.fromApiJsonHelper.extractLocalDateNamed(validFromParamName, element); + baseDataValidator.reset().parameter(validFromParamName).value(validFrom).notNull(); + } + + if (this.fromApiJsonHelper.parameterExists(validTillParamName, element)) { + final LocalDate validTill = this.fromApiJsonHelper.extractLocalDateNamed(validTillParamName, element); + baseDataValidator.reset().parameter(validTillParamName).value(validTill).notNull(); + } + + if (this.fromApiJsonHelper.parameterExists(amountParamName, element)) { + final BigDecimal transferAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(amountParamName, element); + baseDataValidator.reset().parameter(amountParamName).value(transferAmount).positiveAmount(); + } + + if (this.fromApiJsonHelper.parameterExists(statusParamName, element)) { + final Integer status = this.fromApiJsonHelper.extractIntegerNamed(statusParamName, element, Locale.getDefault()); + baseDataValidator.reset().parameter(statusParamName).value(status).notNull().inMinMaxRange(1, 2); + } + + if (this.fromApiJsonHelper.parameterExists(priorityParamName, element)) { + final Integer priority = this.fromApiJsonHelper.extractIntegerNamed(priorityParamName, element, Locale.getDefault()); + baseDataValidator.reset().parameter(priorityParamName).value(priority).notNull().inMinMaxRange(1, 4); + } + + if (this.fromApiJsonHelper.parameterExists(instructionTypeParamName, element)) { + final Integer standingInstructionType = this.fromApiJsonHelper.extractIntegerNamed(instructionTypeParamName, element, + Locale.getDefault()); + baseDataValidator.reset().parameter(instructionTypeParamName).value(standingInstructionType).notNull().inMinMaxRange(1, 2); + } + + if (this.fromApiJsonHelper.parameterExists(recurrenceTypeParamName, element)) { + final Integer recurrenceType = this.fromApiJsonHelper + .extractIntegerNamed(recurrenceTypeParamName, element, Locale.getDefault()); + baseDataValidator.reset().parameter(recurrenceTypeParamName).value(recurrenceType).notNull().inMinMaxRange(1, 2); + } + + if (this.fromApiJsonHelper.parameterExists(recurrenceFrequencyParamName, element)) { + final Integer recurrenceFrequency = this.fromApiJsonHelper.extractIntegerNamed(recurrenceFrequencyParamName, element, + Locale.getDefault()); + baseDataValidator.reset().parameter(recurrenceFrequencyParamName).value(recurrenceFrequency).inMinMaxRange(0, 3); + } + + if (this.fromApiJsonHelper.parameterExists(recurrenceIntervalParamName, element)) { + final Integer recurrenceInterval = this.fromApiJsonHelper.extractIntegerNamed(recurrenceIntervalParamName, element, + Locale.getDefault()); + baseDataValidator.reset().parameter(recurrenceIntervalParamName).value(recurrenceInterval).integerGreaterThanZero(); + } + + if (this.fromApiJsonHelper.parameterExists(nameParamName, element)) { + final String name = this.fromApiJsonHelper.extractStringNamed(nameParamName, element); + baseDataValidator.reset().parameter(nameParamName).value(name).notNull(); + } + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + 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/account/data/StandingInstructionDuesData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/StandingInstructionDuesData.java new file mode 100755 index 000000000..47a7142f6 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/StandingInstructionDuesData.java @@ -0,0 +1,25 @@ +package org.mifosplatform.portfolio.account.data; + +import java.math.BigDecimal; + +import org.joda.time.LocalDate; + +public class StandingInstructionDuesData { + + private final LocalDate dueDate; + private final BigDecimal totalDueAmount; + + public StandingInstructionDuesData(final LocalDate dueDate, final BigDecimal totalDueAmount) { + this.dueDate = dueDate; + this.totalDueAmount = totalDueAmount; + } + + public LocalDate dueDate() { + return this.dueDate; + } + + public BigDecimal totalDueAmount() { + return this.totalDueAmount; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransfer.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransfer.java deleted file mode 100644 index 3d3ad317a..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransfer.java +++ /dev/null @@ -1,182 +0,0 @@ -/** - * 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.account.domain; - -import java.math.BigDecimal; -import java.util.Date; - -import javax.persistence.Column; -import javax.persistence.Embedded; -import javax.persistence.Entity; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; - -import org.joda.time.LocalDate; -import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; -import org.mifosplatform.organisation.monetary.domain.Money; -import org.mifosplatform.organisation.office.domain.Office; -import org.mifosplatform.portfolio.client.domain.Client; -import org.mifosplatform.portfolio.loanaccount.domain.Loan; -import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; -import org.mifosplatform.portfolio.savings.domain.SavingsAccount; -import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction; -import org.springframework.data.jpa.domain.AbstractPersistable; - -@Entity -@Table(name = "m_savings_account_transfer") -public class AccountTransfer extends AbstractPersistable { - - @ManyToOne - @JoinColumn(name = "from_office_id", nullable = false) - private Office fromOffice; - - @ManyToOne - @JoinColumn(name = "from_client_id", nullable = false) - private Client fromClient; - - @ManyToOne - @JoinColumn(name = "from_savings_account_id", nullable = true) - private SavingsAccount fromAccount; - - @ManyToOne - @JoinColumn(name = "to_office_id", nullable = false) - private Office toOffice; - - @ManyToOne - @JoinColumn(name = "to_client_id", nullable = false) - private Client toClient; - - @ManyToOne - @JoinColumn(name = "to_savings_account_id", nullable = true) - private SavingsAccount toSavingsAccount; - - @ManyToOne - @JoinColumn(name = "from_savings_transaction_id", nullable = true) - private SavingsAccountTransaction fromTransaction; - - @ManyToOne - @JoinColumn(name = "to_savings_transaction_id", nullable = true) - private SavingsAccountTransaction toSavingsTransaction; - - @ManyToOne - @JoinColumn(name = "to_loan_account_id", nullable = true) - private Loan toLoanAccount; - - @ManyToOne - @JoinColumn(name = "from_loan_account_id", nullable = true) - private Loan fromLoanAccount; - - @ManyToOne - @JoinColumn(name = "to_loan_transaction_id", nullable = true) - private LoanTransaction toLoanTransaction; - - @ManyToOne - @JoinColumn(name = "from_loan_transaction_id", nullable = true) - private LoanTransaction fromLoanTransaction; - - @Column(name = "is_reversed", nullable = false) - private boolean reversed = false; - - @Temporal(TemporalType.DATE) - @Column(name = "transaction_date") - private Date date; - - @Embedded - private MonetaryCurrency currency; - - @Column(name = "amount", scale = 6, precision = 19, nullable = false) - private BigDecimal amount; - - @Column(name = "description", length = 100) - private String description; - - public static AccountTransfer savingsToSavingsTransfer(final Office fromOffice, final Client fromClient, - final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, - final SavingsAccountTransaction withdrawal, final SavingsAccountTransaction deposit, final LocalDate transactionDate, - final Money transactionAmount, final String description) { - - return new AccountTransfer(fromOffice, fromClient, fromSavingsAccount, null, toOffice, toClient, toSavingsAccount, null, - withdrawal, deposit, null, null, transactionDate, transactionAmount, description); - } - - public static AccountTransfer savingsToLoanTransfer(final Office fromOffice, final Client fromClient, - final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final Loan toLoanAccount, - final SavingsAccountTransaction withdrawal, final LoanTransaction loanRepaymentTransaction, final LocalDate transactionDate, - final Money transactionAmount, final String description) { - return new AccountTransfer(fromOffice, fromClient, fromSavingsAccount, null, toOffice, toClient, null, toLoanAccount, withdrawal, - null, loanRepaymentTransaction, null, transactionDate, transactionAmount, description); - } - - public static AccountTransfer LoanTosavingsTransfer(final Office fromOffice, final Client fromClient, final Loan fromLoanAccount, - final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, final SavingsAccountTransaction deposit, - final LoanTransaction loanRefundTransaction, final LocalDate transactionDate, final Money transactionAmount, - final String description) { - return new AccountTransfer(fromOffice, fromClient, null, fromLoanAccount, toOffice, toClient, toSavingsAccount, null, null, - deposit, null, loanRefundTransaction, transactionDate, transactionAmount, description); - } - - protected AccountTransfer() { - // - } - - private AccountTransfer(final Office fromOffice, final Client fromClient, final SavingsAccount fromSavingsAccount, - final Loan fromLoanAccount, final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, - final Loan toLoanAccount, final SavingsAccountTransaction withdrawal, final SavingsAccountTransaction deposit, - final LoanTransaction loanRepaymentTransaction, final LoanTransaction loanRefundTransaction, final LocalDate transactionDate, - final Money transactionAmount, final String description) { - this.fromOffice = fromOffice; - this.fromClient = fromClient; - this.fromAccount = fromSavingsAccount; - this.fromLoanAccount = fromLoanAccount; - this.fromLoanTransaction = loanRefundTransaction; - this.toOffice = toOffice; - this.toClient = toClient; - this.toSavingsAccount = toSavingsAccount; - this.fromTransaction = withdrawal; - this.toSavingsTransaction = deposit; - this.toLoanAccount = toLoanAccount; - this.toLoanTransaction = loanRepaymentTransaction; - this.date = transactionDate.toDate(); - this.currency = transactionAmount.getCurrency(); - this.amount = transactionAmount.getAmountDefaultedToNullIfZero(); - this.description = description; - } - - public LoanTransaction getFromLoanTransaction() { - return this.fromLoanTransaction; - } - - public SavingsAccountTransaction getFromTransaction() { - return this.fromTransaction; - } - - public LoanTransaction getToLoanTransaction() { - return this.toLoanTransaction; - } - - public SavingsAccountTransaction getToSavingsTransaction() { - return this.toSavingsTransaction; - } - - public void reverse() { - this.reversed = true; - } - - public SavingsAccount getToSavingsAccount() { - return this.toSavingsAccount; - } - - public SavingsAccount getFromAccount() { - return this.fromAccount; - } - - public void updateToLoanTransaction(LoanTransaction toLoanTransaction) { - this.toLoanTransaction = toLoanTransaction; - } -} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferAssembler.java index 3739d551d..6e20c8eef 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferAssembler.java @@ -5,130 +5,72 @@ */ package org.mifosplatform.portfolio.account.domain; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromAccountIdParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromClientIdParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromOfficeIdParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toAccountIdParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toClientIdParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toOfficeIdParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferAmountParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferDateParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferDescriptionParamName; +import static org.mifosplatform.portfolio.account.api.AccountTransfersApiConstants.transferAmountParamName; +import static org.mifosplatform.portfolio.account.api.AccountTransfersApiConstants.transferDateParamName; +import static org.mifosplatform.portfolio.account.api.AccountTransfersApiConstants.transferDescriptionParamName; import java.math.BigDecimal; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.api.JsonCommand; -import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.organisation.monetary.domain.Money; -import org.mifosplatform.organisation.office.domain.Office; -import org.mifosplatform.organisation.office.domain.OfficeRepository; import org.mifosplatform.portfolio.account.data.AccountTransferDTO; -import org.mifosplatform.portfolio.client.domain.Client; -import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; import org.mifosplatform.portfolio.loanaccount.domain.Loan; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; import org.mifosplatform.portfolio.savings.domain.SavingsAccount; -import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler; import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import com.google.gson.JsonElement; - @Service public class AccountTransferAssembler { - private final ClientRepositoryWrapper clientRepository; - private final OfficeRepository officeRepository; - private final SavingsAccountAssembler savingsAccountAssembler; - private final FromJsonHelper fromApiJsonHelper; + private final AccountTransferDetailAssembler accountTransferDetailAssembler; @Autowired - public AccountTransferAssembler(final ClientRepositoryWrapper clientRepository, final OfficeRepository officeRepository, - final SavingsAccountAssembler savingsAccountAssembler, final FromJsonHelper fromApiJsonHelper) { - this.clientRepository = clientRepository; - this.officeRepository = officeRepository; - this.savingsAccountAssembler = savingsAccountAssembler; - this.fromApiJsonHelper = fromApiJsonHelper; + public AccountTransferAssembler(final AccountTransferDetailAssembler accountTransferDetailAssembler) { + this.accountTransferDetailAssembler = accountTransferDetailAssembler; } - public AccountTransfer assembleSavingsToSavingsTransfer(final JsonCommand command, final SavingsAccountTransaction withdrawal, - final SavingsAccountTransaction deposit) { + public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command, final SavingsAccount fromSavingsAccount, + final SavingsAccount toSavingsAccount, final SavingsAccountTransaction withdrawal, final SavingsAccountTransaction deposit) { - final JsonElement element = command.parsedJson(); - - final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); - final Office fromOffice = this.officeRepository.findOne(fromOfficeId); - - final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); - final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId); - - final Long fromSavingsId = command.longValueOfParameterNamed(fromAccountIdParamName); - final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsId); - - final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); - final Office toOffice = this.officeRepository.findOne(toOfficeId); - - final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); - final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId); - - final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName); - final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId); + final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToSavingsTransfer(command, + fromSavingsAccount, toSavingsAccount); final LocalDate transactionDate = command.localDateValueOfParameterNamed(transferDateParamName); final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(transferAmountParamName); final Money transactionMonetaryAmount = Money.of(fromSavingsAccount.getCurrency(), transactionAmount); final String description = command.stringValueOfParameterNamed(transferDescriptionParamName); - - return AccountTransfer.savingsToSavingsTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toSavingsAccount, + AccountTransferTransaction accountTransferTransaction = AccountTransferTransaction.savingsToSavingsTransfer(accountTransferDetails, withdrawal, deposit, transactionDate, transactionMonetaryAmount, description); + accountTransferDetails.addAccountTransferTransaction(accountTransferTransaction); + return accountTransferDetails; } - public AccountTransfer assembleSavingsToLoanTransfer(final JsonCommand command, final SavingsAccount fromSavingsAccount, + public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command, final SavingsAccount fromSavingsAccount, final Loan toLoanAccount, final SavingsAccountTransaction withdrawal, final LoanTransaction loanRepaymentTransaction) { - final JsonElement element = command.parsedJson(); - - final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); - final Office fromOffice = this.officeRepository.findOne(fromOfficeId); - - final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); - final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId); - - final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); - final Office toOffice = this.officeRepository.findOne(toOfficeId); - - final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); - final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId); - + final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToLoanTransfer(command, + fromSavingsAccount, toLoanAccount); final LocalDate transactionDate = command.localDateValueOfParameterNamed(transferDateParamName); final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(transferAmountParamName); final Money transactionMonetaryAmount = Money.of(fromSavingsAccount.getCurrency(), transactionAmount); final String description = command.stringValueOfParameterNamed(transferDescriptionParamName); - return AccountTransfer.savingsToLoanTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toLoanAccount, + AccountTransferTransaction accountTransferTransaction = AccountTransferTransaction.savingsToLoanTransfer(accountTransferDetails, withdrawal, loanRepaymentTransaction, transactionDate, transactionMonetaryAmount, description); + accountTransferDetails.addAccountTransferTransaction(accountTransferTransaction); + return accountTransferDetails; } - public AccountTransfer assembleLoanToSavingsTransfer(final JsonCommand command, final Loan fromLoanAccount, + public AccountTransferDetails assembleLoanToSavingsTransfer(final JsonCommand command, final Loan fromLoanAccount, final SavingsAccount toSavingsAccount, final SavingsAccountTransaction deposit, final LoanTransaction loanRefundTransaction) { - final JsonElement element = command.parsedJson(); - - final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); - final Office fromOffice = this.officeRepository.findOne(fromOfficeId); - - final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); - final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId); - - final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); - final Office toOffice = this.officeRepository.findOne(toOfficeId); - - final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); - final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId); + final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleLoanToSavingsTransfer(command, + fromLoanAccount, toSavingsAccount); final LocalDate transactionDate = command.localDateValueOfParameterNamed(transferDateParamName); final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(transferAmountParamName); @@ -136,22 +78,58 @@ public class AccountTransferAssembler { final String description = command.stringValueOfParameterNamed(transferDescriptionParamName); - return AccountTransfer.LoanTosavingsTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toSavingsAccount, + AccountTransferTransaction accountTransferTransaction = AccountTransferTransaction.LoanTosavingsTransfer(accountTransferDetails, deposit, loanRefundTransaction, transactionDate, transactionMonetaryAmount, description); + accountTransferDetails.addAccountTransferTransaction(accountTransferTransaction); + return accountTransferDetails; } - public AccountTransfer assembleSavingsToLoanTransfer(final AccountTransferDTO accountTransferDTO, + public AccountTransferDetails assembleSavingsToLoanTransfer(final AccountTransferDTO accountTransferDTO, final SavingsAccount fromSavingsAccount, final Loan toLoanAccount, final SavingsAccountTransaction savingsAccountTransaction, final LoanTransaction loanTransaction) { - final Office fromOffice = fromSavingsAccount.office(); - final Client fromClient = fromSavingsAccount.getClient(); - final Office toOffice = toLoanAccount.getOffice(); - final Client toClient = toLoanAccount.client(); - final Money transactionMonetaryAmount = Money.of(fromSavingsAccount.getCurrency(), accountTransferDTO.getTransactionAmount()); - return AccountTransfer.savingsToLoanTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toLoanAccount, + AccountTransferDetails accountTransferDetails = accountTransferDTO.getAccountTransferDetails(); + if (accountTransferDetails == null) { + accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToLoanTransfer(fromSavingsAccount, toLoanAccount, + accountTransferDTO.getTransferType()); + } + AccountTransferTransaction accountTransferTransaction = AccountTransferTransaction.savingsToLoanTransfer(accountTransferDetails, savingsAccountTransaction, loanTransaction, accountTransferDTO.getTransactionDate(), transactionMonetaryAmount, accountTransferDTO.getDescription()); + accountTransferDetails.addAccountTransferTransaction(accountTransferTransaction); + return accountTransferDetails; + } + + public AccountTransferDetails assembleSavingsToSavingsTransfer(final AccountTransferDTO accountTransferDTO, + final SavingsAccount fromSavingsAccount, final SavingsAccount toSavingsAccount, final SavingsAccountTransaction withdrawal, + final SavingsAccountTransaction deposit) { + final Money transactionMonetaryAmount = Money.of(fromSavingsAccount.getCurrency(), accountTransferDTO.getTransactionAmount()); + AccountTransferDetails accountTransferDetails = accountTransferDTO.getAccountTransferDetails(); + if (accountTransferDetails == null) { + accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToSavingsTransfer(fromSavingsAccount, + toSavingsAccount, accountTransferDTO.getTransferType()); + } + + AccountTransferTransaction accountTransferTransaction = AccountTransferTransaction.savingsToSavingsTransfer(accountTransferDetails, + withdrawal, deposit, accountTransferDTO.getTransactionDate(), transactionMonetaryAmount, + accountTransferDTO.getDescription()); + accountTransferDetails.addAccountTransferTransaction(accountTransferTransaction); + return accountTransferDetails; + } + + public AccountTransferDetails assembleLoanToSavingsTransfer(final AccountTransferDTO accountTransferDTO, final Loan fromLoanAccount, + final SavingsAccount toSavingsAccount, final SavingsAccountTransaction deposit, final LoanTransaction loanRefundTransaction) { + final Money transactionMonetaryAmount = Money.of(fromLoanAccount.getCurrency(), accountTransferDTO.getTransactionAmount()); + AccountTransferDetails accountTransferDetails = accountTransferDTO.getAccountTransferDetails(); + if (accountTransferDetails == null) { + accountTransferDetails = this.accountTransferDetailAssembler.assembleLoanToSavingsTransfer(fromLoanAccount, toSavingsAccount, + accountTransferDTO.getTransferType()); + } + AccountTransferTransaction accountTransferTransaction = AccountTransferTransaction.LoanTosavingsTransfer(accountTransferDetails, + deposit, loanRefundTransaction, accountTransferDTO.getTransactionDate(), transactionMonetaryAmount, + accountTransferDTO.getDescription()); + accountTransferDetails.addAccountTransferTransaction(accountTransferTransaction); + return accountTransferDetails; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferDetailAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferDetailAssembler.java new file mode 100755 index 000000000..df9a15580 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferDetailAssembler.java @@ -0,0 +1,191 @@ +/** + * 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.account.domain; + +import static org.mifosplatform.portfolio.account.AccountDetailConstants.fromAccountIdParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.fromClientIdParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.fromOfficeIdParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.toAccountIdParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.toClientIdParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.toOfficeIdParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.transferTypeParamName; + +import java.util.Locale; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.organisation.office.domain.Office; +import org.mifosplatform.organisation.office.domain.OfficeRepository; +import org.mifosplatform.portfolio.client.domain.Client; +import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; +import org.mifosplatform.portfolio.loanaccount.domain.Loan; +import org.mifosplatform.portfolio.loanaccount.service.LoanAssembler; +import org.mifosplatform.portfolio.savings.domain.SavingsAccount; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.google.gson.JsonElement; + +@Service +public class AccountTransferDetailAssembler { + + private final ClientRepositoryWrapper clientRepository; + private final OfficeRepository officeRepository; + private final SavingsAccountAssembler savingsAccountAssembler; + private final FromJsonHelper fromApiJsonHelper; + private final LoanAssembler loanAccountAssembler; + + @Autowired + public AccountTransferDetailAssembler(final ClientRepositoryWrapper clientRepository, final OfficeRepository officeRepository, + final SavingsAccountAssembler savingsAccountAssembler, final FromJsonHelper fromApiJsonHelper, + final LoanAssembler loanAccountAssembler) { + this.clientRepository = clientRepository; + this.officeRepository = officeRepository; + this.savingsAccountAssembler = savingsAccountAssembler; + this.fromApiJsonHelper = fromApiJsonHelper; + this.loanAccountAssembler = loanAccountAssembler; + } + + public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command) { + + final Long fromSavingsId = command.longValueOfParameterNamed(fromAccountIdParamName); + final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsId); + + final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName); + final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId); + + return assembleSavingsToSavingsTransfer(command, fromSavingsAccount, toSavingsAccount); + + } + + public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command) { + + final Long fromSavingsAccountId = command.longValueOfParameterNamed(fromAccountIdParamName); + final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsAccountId); + + final Long toLoanAccountId = command.longValueOfParameterNamed(toAccountIdParamName); + final Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(toLoanAccountId); + + return assembleSavingsToLoanTransfer(command, fromSavingsAccount, toLoanAccount); + + } + + public AccountTransferDetails assembleLoanToSavingsTransfer(final JsonCommand command) { + + final Long fromLoanAccountId = command.longValueOfParameterNamed(fromAccountIdParamName); + final Loan fromLoanAccount = this.loanAccountAssembler.assembleFrom(fromLoanAccountId); + + final Long toSavingsAccountId = command.longValueOfParameterNamed(toAccountIdParamName); + final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsAccountId); + + return assembleLoanToSavingsTransfer(command, fromLoanAccount, toSavingsAccount); + } + + public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command, final SavingsAccount fromSavingsAccount, + final SavingsAccount toSavingsAccount) { + + final JsonElement element = command.parsedJson(); + + final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); + final Office fromOffice = this.officeRepository.findOne(fromOfficeId); + + final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); + final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId); + + final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); + final Office toOffice = this.officeRepository.findOne(toOfficeId); + + final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); + final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId); + + final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault()); + + return AccountTransferDetails.savingsToSavingsTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, + toSavingsAccount, transfertype); + + } + + public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command, final SavingsAccount fromSavingsAccount, + final Loan toLoanAccount) { + + final JsonElement element = command.parsedJson(); + + final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); + final Office fromOffice = this.officeRepository.findOne(fromOfficeId); + + final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); + final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId); + + final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); + final Office toOffice = this.officeRepository.findOne(toOfficeId); + + final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); + final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId); + + final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault()); + + return AccountTransferDetails.savingsToLoanTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toLoanAccount, + transfertype); + + } + + public AccountTransferDetails assembleLoanToSavingsTransfer(final JsonCommand command, final Loan fromLoanAccount, + final SavingsAccount toSavingsAccount) { + + final JsonElement element = command.parsedJson(); + + final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); + final Office fromOffice = this.officeRepository.findOne(fromOfficeId); + + final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); + final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId); + + final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); + final Office toOffice = this.officeRepository.findOne(toOfficeId); + + final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); + final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId); + final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault()); + + return AccountTransferDetails.LoanTosavingsTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toSavingsAccount, + transfertype); + } + + public AccountTransferDetails assembleSavingsToLoanTransfer(final SavingsAccount fromSavingsAccount, final Loan toLoanAccount, + Integer transferType) { + final Office fromOffice = fromSavingsAccount.office(); + final Client fromClient = fromSavingsAccount.getClient(); + final Office toOffice = toLoanAccount.getOffice(); + final Client toClient = toLoanAccount.client(); + + return AccountTransferDetails.savingsToLoanTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toLoanAccount, + transferType); + + } + + public AccountTransferDetails assembleSavingsToSavingsTransfer(final SavingsAccount fromSavingsAccount, + final SavingsAccount toSavingsAccount, Integer transferType) { + final Office fromOffice = fromSavingsAccount.office(); + final Client fromClient = fromSavingsAccount.getClient(); + final Office toOffice = toSavingsAccount.office(); + final Client toClient = toSavingsAccount.getClient(); + + return AccountTransferDetails.savingsToSavingsTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, + toSavingsAccount, transferType); + } + + public AccountTransferDetails assembleLoanToSavingsTransfer(final Loan fromLoanAccount, final SavingsAccount toSavingsAccount, + Integer transferType) { + final Office fromOffice = fromLoanAccount.getOffice(); + final Client fromClient = fromLoanAccount.client(); + final Office toOffice = toSavingsAccount.office(); + final Client toClient = toSavingsAccount.getClient(); + + return AccountTransferDetails.LoanTosavingsTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toSavingsAccount, + transferType); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferDetailRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferDetailRepository.java new file mode 100755 index 000000000..cc205ed7c --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferDetailRepository.java @@ -0,0 +1,12 @@ +/** + * 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.account.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface AccountTransferDetailRepository extends JpaRepository, + JpaSpecificationExecutor {} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferDetails.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferDetails.java new file mode 100755 index 000000000..bd8d29a75 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferDetails.java @@ -0,0 +1,153 @@ +/** + * 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.account.domain; + +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.OneToOne; +import javax.persistence.Table; + +import org.hibernate.annotations.LazyCollection; +import org.hibernate.annotations.LazyCollectionOption; +import org.mifosplatform.organisation.office.domain.Office; +import org.mifosplatform.portfolio.client.domain.Client; +import org.mifosplatform.portfolio.loanaccount.domain.Loan; +import org.mifosplatform.portfolio.savings.domain.SavingsAccount; +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "m_account_transfer_details") +public class AccountTransferDetails extends AbstractPersistable { + + @SuppressWarnings("unused") + @ManyToOne + @JoinColumn(name = "from_office_id", nullable = false) + private Office fromOffice; + + @SuppressWarnings("unused") + @ManyToOne + @JoinColumn(name = "from_client_id", nullable = false) + private Client fromClient; + + @ManyToOne + @JoinColumn(name = "from_savings_account_id", nullable = true) + private SavingsAccount fromSavingsAccount; + + @SuppressWarnings("unused") + @ManyToOne + @JoinColumn(name = "to_office_id", nullable = false) + private Office toOffice; + + @SuppressWarnings("unused") + @ManyToOne + @JoinColumn(name = "to_client_id", nullable = false) + private Client toClient; + + @ManyToOne + @JoinColumn(name = "to_savings_account_id", nullable = true) + private SavingsAccount toSavingsAccount; + + @ManyToOne + @JoinColumn(name = "to_loan_account_id", nullable = true) + private Loan toLoanAccount; + + @ManyToOne + @JoinColumn(name = "from_loan_account_id", nullable = true) + private Loan fromLoanAccount; + + @Column(name = "transfer_type") + private Integer transferType; + + @LazyCollection(LazyCollectionOption.FALSE) + @OneToMany(cascade = CascadeType.ALL, mappedBy = "accountTransferDetails", orphanRemoval = true) + private final List accountTransferTransactions = new ArrayList(); + + @OneToOne(mappedBy = "accountTransferDetails", cascade = CascadeType.ALL, optional = true, orphanRemoval = true, fetch = FetchType.LAZY) + private AccountTransferStandingInstruction accountTransferStandingInstruction; + + public static AccountTransferDetails savingsToSavingsTransfer(final Office fromOffice, final Client fromClient, + final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, + Integer transferType) { + + return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, toOffice, toClient, toSavingsAccount, null, + transferType, null); + } + + public static AccountTransferDetails savingsToLoanTransfer(final Office fromOffice, final Client fromClient, + final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final Loan toLoanAccount, + Integer transferType) { + return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, toOffice, toClient, null, toLoanAccount, + transferType, null); + } + + public static AccountTransferDetails LoanTosavingsTransfer(final Office fromOffice, final Client fromClient, + final Loan fromLoanAccount, final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, + Integer transferType) { + return new AccountTransferDetails(fromOffice, fromClient, null, fromLoanAccount, toOffice, toClient, toSavingsAccount, null, + transferType, null); + } + + protected AccountTransferDetails() { + // + } + + private AccountTransferDetails(final Office fromOffice, final Client fromClient, final SavingsAccount fromSavingsAccount, + final Loan fromLoanAccount, final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, + final Loan toLoanAccount, final Integer transferType, + final AccountTransferStandingInstruction accountTransferStandingInstruction) { + this.fromOffice = fromOffice; + this.fromClient = fromClient; + this.fromSavingsAccount = fromSavingsAccount; + this.fromLoanAccount = fromLoanAccount; + this.toOffice = toOffice; + this.toClient = toClient; + this.toSavingsAccount = toSavingsAccount; + this.toLoanAccount = toLoanAccount; + this.transferType = transferType; + this.accountTransferStandingInstruction = accountTransferStandingInstruction; + } + + public SavingsAccount toSavingsAccount() { + return this.toSavingsAccount; + } + + public SavingsAccount fromSavingsAccount() { + return this.fromSavingsAccount; + } + + public void addAccountTransferTransaction(AccountTransferTransaction accountTransferTransaction) { + this.accountTransferTransactions.add(accountTransferTransaction); + } + + public void updateAccountTransferStandingInstruction(final AccountTransferStandingInstruction accountTransferStandingInstruction) { + this.accountTransferStandingInstruction = accountTransferStandingInstruction; + } + + public Loan toLoanAccount() { + return this.toLoanAccount; + } + + public Loan fromLoanAccount() { + return this.fromLoanAccount; + } + + public AccountTransferStandingInstruction accountTransferStandingInstruction() { + return this.accountTransferStandingInstruction; + } + + public AccountTransferType transferType() { + return AccountTransferType.fromInt(this.transferType); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferRecurrenceType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferRecurrenceType.java new file mode 100755 index 000000000..dd47e296f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferRecurrenceType.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.account.domain; + +/** + * Enum representation of loan status states. + */ +public enum AccountTransferRecurrenceType { + + INVALID(0, "accountTransferRecurrenceType.invalid"), // + PERIODIC(1, "accountTransferRecurrenceType.periodic"), // + AS_PER_DUES(2, "accountTransferRecurrenceType.as.per.dues"); // + + private final Integer value; + private final String code; + + public static AccountTransferRecurrenceType fromInt(final Integer statusValue) { + + AccountTransferRecurrenceType enumeration = AccountTransferRecurrenceType.INVALID; + switch (statusValue) { + case 1: + enumeration = AccountTransferRecurrenceType.PERIODIC; + break; + case 2: + enumeration = AccountTransferRecurrenceType.AS_PER_DUES; + break; + } + return enumeration; + } + + private AccountTransferRecurrenceType(final Integer value, final String code) { + this.value = value; + this.code = code; + } + + public boolean hasStateOf(final AccountTransferRecurrenceType state) { + return this.value.equals(state.getValue()); + } + + public Integer getValue() { + return this.value; + } + + public String getCode() { + return this.code; + } + + public boolean isPeriodicRecurrence() { + return this.value.equals(AccountTransferRecurrenceType.PERIODIC.getValue()); + } + + public boolean isDuesRecurrence() { + return this.value.equals(AccountTransferRecurrenceType.AS_PER_DUES.getValue()); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferRepository.java index 7388291e6..8ec735b15 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferRepository.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferRepository.java @@ -12,14 +12,14 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -public interface AccountTransferRepository extends JpaRepository, JpaSpecificationExecutor { +public interface AccountTransferRepository extends JpaRepository, JpaSpecificationExecutor { - @Query("from AccountTransfer at where at.fromLoanAccount.id= :accountNumber and at.reversed=false") - List findByFromLoanId(@Param("accountNumber") Long accountNumber); + @Query("from AccountTransferTransaction att where att.accountTransferDetails.fromLoanAccount.id= :accountNumber and att.reversed=false") + List findByFromLoanId(@Param("accountNumber") Long accountNumber); - @Query("from AccountTransfer at where (at.fromLoanAccount.id= :accountNumber or at.toLoanAccount.id=:accountNumber) and at.reversed=false") - List findAllByLoanId(@Param("accountNumber") Long accountNumber); + @Query("from AccountTransferTransaction att where (att.accountTransferDetails.fromLoanAccount.id= :accountNumber or att.accountTransferDetails.toLoanAccount.id=:accountNumber) and att.reversed=false") + List findAllByLoanId(@Param("accountNumber") Long accountNumber); - @Query("from AccountTransfer at where at.toLoanTransaction.id= :loanTransactionId and at.reversed=false") - AccountTransfer findByToLoanTransactionId(@Param("loanTransactionId") Long loanTransactionId); + @Query("from AccountTransferTransaction att where att.toLoanTransaction.id= :loanTransactionId and att.reversed=false") + AccountTransferTransaction findByToLoanTransactionId(@Param("loanTransactionId") Long loanTransactionId); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferStandingInstruction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferStandingInstruction.java new file mode 100755 index 000000000..9b0ccb5b4 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferStandingInstruction.java @@ -0,0 +1,278 @@ +package org.mifosplatform.portfolio.account.domain; + +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.amountParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.instructionTypeParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.priorityParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.recurrenceFrequencyParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.recurrenceIntervalParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.recurrenceOnMonthDayParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.recurrenceTypeParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.statusParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.validFromParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.validTillParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.transferTypeParamName; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import javax.persistence.UniqueConstraint; + +import org.joda.time.LocalDate; +import org.joda.time.MonthDay; +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.common.domain.PeriodFrequencyType; +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "m_account_transfer_standing_instructions", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "name") }) +public class AccountTransferStandingInstruction extends AbstractPersistable { + + @ManyToOne + @JoinColumn(name = "account_transfer_details_id", nullable = true) + private AccountTransferDetails accountTransferDetails; + + @SuppressWarnings("unused") + @Column(name = "name") + private String name; + + @Column(name = "priority") + private Integer priority; + + @Column(name = "instruction_type") + private Integer instructionType; + + @Column(name = "status") + private Integer status; + + @Column(name = "amount", scale = 6, precision = 19, nullable = true) + private BigDecimal amount; + + @Temporal(TemporalType.DATE) + @Column(name = "valid_from") + private Date validFrom; + + @Temporal(TemporalType.DATE) + @Column(name = "valid_till") + private Date validTill; + + @Column(name = "recurrence_type") + private Integer recurrenceType; + + @Column(name = "recurrence_frequency") + private Integer recurrenceFrequency; + + @Column(name = "recurrence_interval") + private Integer recurrenceInterval; + + @Column(name = "recurrence_on_day") + private Integer recurrenceOnDay; + + @Column(name = "recurrence_on_month") + private Integer recurrenceOnMonth; + + @SuppressWarnings("unused") + @Temporal(TemporalType.DATE) + @Column(name = "last_run_date") + private Date latsRunDate; + + protected AccountTransferStandingInstruction() { + + } + + public static AccountTransferStandingInstruction create(final AccountTransferDetails accountTransferDetails, final String name, + final Integer priority, final Integer instructionType, final Integer status, final BigDecimal amount, + final LocalDate validFrom, final LocalDate validTill, final Integer recurrenceType, final Integer recurrenceFrequency, + final Integer recurrenceInterval, final MonthDay recurrenceOnMonthDay) { + Integer recurrenceOnDay = null; + Integer recurrenceOnMonth = null; + if (recurrenceOnMonthDay != null) { + recurrenceOnDay = recurrenceOnMonthDay.getDayOfMonth(); + recurrenceOnMonth = recurrenceOnMonthDay.getMonthOfYear(); + } + return new AccountTransferStandingInstruction(accountTransferDetails, name, priority, instructionType, status, amount, validFrom, + validTill, recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnDay, recurrenceOnMonth); + } + + private AccountTransferStandingInstruction(final AccountTransferDetails accountTransferDetails, final String name, + final Integer priority, final Integer instructionType, final Integer status, final BigDecimal amount, + final LocalDate validFrom, final LocalDate validTill, final Integer recurrenceType, final Integer recurrenceFrequency, + final Integer recurrenceInterval, final Integer recurrenceOnDay, final Integer recurrenceOnMonth) { + this.accountTransferDetails = accountTransferDetails; + this.name = name; + this.priority = priority; + this.instructionType = instructionType; + this.status = status; + this.amount = amount; + if (validFrom != null) { + this.validFrom = validFrom.toDate(); + } + if (validTill == null) { + this.validTill = null; + } else { + this.validTill = validTill.toDate(); + } + this.recurrenceType = recurrenceType; + this.recurrenceFrequency = recurrenceFrequency; + this.recurrenceInterval = recurrenceInterval; + this.recurrenceOnDay = recurrenceOnDay; + this.recurrenceOnMonth = recurrenceOnMonth; + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(STANDING_INSTRUCTION_RESOURCE_NAME); + + validateDependencies(baseDataValidator); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + public Map update(JsonCommand command) { + final Map actualChanges = new HashMap(); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(STANDING_INSTRUCTION_RESOURCE_NAME); + + if (StandingInstructionStatus.fromInt(this.status).isDeleted()) { + baseDataValidator.reset().parameter(statusParamName).failWithCode("can.not.modify.once.deleted"); + } + + if (command.isChangeInDateParameterNamed(validFromParamName, this.validFrom)) { + final LocalDate newValue = command.localDateValueOfParameterNamed(validFromParamName); + actualChanges.put(validFromParamName, newValue); + this.validFrom = newValue.toDate(); + } + + if (command.isChangeInDateParameterNamed(validTillParamName, this.validTill)) { + final LocalDate newValue = command.localDateValueOfParameterNamed(validTillParamName); + actualChanges.put(validTillParamName, newValue); + this.validTill = newValue.toDate(); + } + + if (command.isChangeInBigDecimalParameterNamed(amountParamName, this.amount)) { + final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(amountParamName); + actualChanges.put(amountParamName, newValue); + this.amount = newValue; + } + + if (command.isChangeInIntegerParameterNamed(statusParamName, this.status)) { + final Integer newValue = command.integerValueOfParameterNamed(statusParamName); + actualChanges.put(statusParamName, newValue); + this.status = newValue; + } + + if (command.isChangeInIntegerParameterNamed(priorityParamName, this.priority)) { + final Integer newValue = command.integerValueOfParameterNamed(priorityParamName); + actualChanges.put(priorityParamName, newValue); + this.priority = newValue; + } + + if (command.isChangeInIntegerParameterNamed(instructionTypeParamName, this.instructionType)) { + final Integer newValue = command.integerValueOfParameterNamed(instructionTypeParamName); + actualChanges.put(instructionTypeParamName, newValue); + this.instructionType = newValue; + } + + if (command.isChangeInIntegerParameterNamed(recurrenceTypeParamName, this.recurrenceType)) { + final Integer newValue = command.integerValueOfParameterNamed(recurrenceTypeParamName); + actualChanges.put(recurrenceTypeParamName, newValue); + this.recurrenceType = newValue; + } + + if (command.isChangeInIntegerParameterNamed(recurrenceFrequencyParamName, this.recurrenceFrequency)) { + final Integer newValue = command.integerValueOfParameterNamed(recurrenceFrequencyParamName); + actualChanges.put(recurrenceFrequencyParamName, newValue); + this.recurrenceFrequency = newValue; + } + + if (command.hasParameter(recurrenceOnMonthDayParamName)) { + final MonthDay monthDay = command.extractMonthDayNamed(recurrenceOnMonthDayParamName); + final String actualValueEntered = command.stringValueOfParameterNamed(recurrenceOnMonthDayParamName); + final Integer dayOfMonthValue = monthDay.getDayOfMonth(); + if (this.recurrenceOnDay != dayOfMonthValue) { + actualChanges.put(recurrenceOnMonthDayParamName, actualValueEntered); + this.recurrenceOnDay = dayOfMonthValue; + } + + final Integer monthOfYear = monthDay.getMonthOfYear(); + if (this.recurrenceOnMonth != monthOfYear) { + actualChanges.put(recurrenceOnMonthDayParamName, actualValueEntered); + this.recurrenceOnMonth = monthOfYear; + } + } + + if (command.isChangeInIntegerParameterNamed(recurrenceIntervalParamName, this.recurrenceInterval)) { + final Integer newValue = command.integerValueOfParameterNamed(recurrenceIntervalParamName); + actualChanges.put(recurrenceIntervalParamName, newValue); + this.recurrenceInterval = newValue; + } + validateDependencies(baseDataValidator); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + return actualChanges; + } + + private void validateDependencies(final DataValidatorBuilder baseDataValidator) { + + if (this.validTill != null && this.validFrom != null) { + baseDataValidator.reset().parameter(validTillParamName).value(LocalDate.fromDateFields(this.validTill)) + .validateDateAfter(LocalDate.fromDateFields(this.validFrom)); + } + + if (AccountTransferRecurrenceType.fromInt(recurrenceType).isPeriodicRecurrence()) { + baseDataValidator.reset().parameter(recurrenceFrequencyParamName).value(this.recurrenceFrequency).notNull(); + baseDataValidator.reset().parameter(recurrenceIntervalParamName).value(this.recurrenceInterval).notNull(); + if (this.recurrenceFrequency != null) { + PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(this.recurrenceFrequency); + if (frequencyType.isMonthly()) { + baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(this.recurrenceOnDay).notNull(); + } else if (frequencyType.isYearly()) { + baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(this.recurrenceOnDay).notNull(); + baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(this.recurrenceOnMonth).notNull(); + } + } + } + + if (this.accountTransferDetails.toSavingsAccount() != null) { + baseDataValidator.reset().parameter(instructionTypeParamName).value(this.instructionType).notNull().inMinMaxRange(1, 1); + baseDataValidator.reset().parameter(recurrenceTypeParamName).value(this.recurrenceType).notNull().inMinMaxRange(1, 1); + } + + if (StandingInstructionType.fromInt(this.instructionType).isFixedAmoutTransfer()) { + baseDataValidator.reset().parameter(amountParamName).value(this.amount).notNull(); + } + + String errorCode = null; + if (this.accountTransferDetails.transferType().isAccountTransfer() + && (this.accountTransferDetails.fromSavingsAccount() == null || this.accountTransferDetails.toSavingsAccount() == null)) { + errorCode = "not.account.transfer"; + } else if (this.accountTransferDetails.transferType().isLoanRepayment() + && (this.accountTransferDetails.fromSavingsAccount() == null || this.accountTransferDetails.toLoanAccount() == null)) { + errorCode = "not.loan.repayment"; + } + if (errorCode != null) { + baseDataValidator.reset().parameter(transferTypeParamName).failWithCode(errorCode); + } + + } + + public void updateLatsRunDate(Date latsRunDate) { + this.latsRunDate = latsRunDate; + } + + public void updateStatus(Integer status){ + this.status = status; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferTransaction.java new file mode 100644 index 000000000..cfdb91dc4 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferTransaction.java @@ -0,0 +1,140 @@ +/** + * 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.account.domain; + +import java.math.BigDecimal; +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import org.joda.time.LocalDate; +import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; +import org.mifosplatform.organisation.monetary.domain.Money; +import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction; +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "m_account_transfer_transaction") +public class AccountTransferTransaction extends AbstractPersistable { + + @ManyToOne + @JoinColumn(name = "account_transfer_details_id", nullable = true) + private AccountTransferDetails accountTransferDetails; + + @ManyToOne + @JoinColumn(name = "from_savings_transaction_id", nullable = true) + private SavingsAccountTransaction fromSavingsTransaction; + + @ManyToOne + @JoinColumn(name = "to_savings_transaction_id", nullable = true) + private SavingsAccountTransaction toSavingsTransaction; + + @ManyToOne + @JoinColumn(name = "to_loan_transaction_id", nullable = true) + private LoanTransaction toLoanTransaction; + + @ManyToOne + @JoinColumn(name = "from_loan_transaction_id", nullable = true) + private LoanTransaction fromLoanTransaction; + + @SuppressWarnings("unused") + @Column(name = "is_reversed", nullable = false) + private boolean reversed = false; + + @SuppressWarnings("unused") + @Temporal(TemporalType.DATE) + @Column(name = "transaction_date") + private Date date; + + @SuppressWarnings("unused") + @Embedded + private MonetaryCurrency currency; + + @SuppressWarnings("unused") + @Column(name = "amount", scale = 6, precision = 19, nullable = false) + private BigDecimal amount; + + @SuppressWarnings("unused") + @Column(name = "description", length = 100) + private String description; + + public static AccountTransferTransaction savingsToSavingsTransfer(final AccountTransferDetails accountTransferDetails, + final SavingsAccountTransaction withdrawal, final SavingsAccountTransaction deposit, final LocalDate transactionDate, + final Money transactionAmount, final String description) { + + return new AccountTransferTransaction(accountTransferDetails, withdrawal, deposit, null, null, transactionDate, transactionAmount, + description); + } + + public static AccountTransferTransaction savingsToLoanTransfer(final AccountTransferDetails accountTransferDetails, + final SavingsAccountTransaction withdrawal, final LoanTransaction loanRepaymentTransaction, final LocalDate transactionDate, + final Money transactionAmount, final String description) { + return new AccountTransferTransaction(accountTransferDetails, withdrawal, null, loanRepaymentTransaction, null, transactionDate, + transactionAmount, description); + } + + public static AccountTransferTransaction LoanTosavingsTransfer(final AccountTransferDetails accountTransferDetails, + final SavingsAccountTransaction deposit, final LoanTransaction loanRefundTransaction, final LocalDate transactionDate, + final Money transactionAmount, final String description) { + return new AccountTransferTransaction(accountTransferDetails, null, deposit, null, loanRefundTransaction, transactionDate, + transactionAmount, description); + } + + protected AccountTransferTransaction() { + // + } + + private AccountTransferTransaction(final AccountTransferDetails accountTransferDetails, final SavingsAccountTransaction withdrawal, + final SavingsAccountTransaction deposit, final LoanTransaction loanRepaymentTransaction, + final LoanTransaction loanRefundTransaction, final LocalDate transactionDate, final Money transactionAmount, + final String description) { + this.accountTransferDetails = accountTransferDetails; + this.fromLoanTransaction = loanRefundTransaction; + this.fromSavingsTransaction = withdrawal; + this.toSavingsTransaction = deposit; + this.toLoanTransaction = loanRepaymentTransaction; + this.date = transactionDate.toDate(); + this.currency = transactionAmount.getCurrency(); + this.amount = transactionAmount.getAmountDefaultedToNullIfZero(); + this.description = description; + } + + public LoanTransaction getFromLoanTransaction() { + return this.fromLoanTransaction; + } + + public SavingsAccountTransaction getFromTransaction() { + return this.fromSavingsTransaction; + } + + public LoanTransaction getToLoanTransaction() { + return this.toLoanTransaction; + } + + public SavingsAccountTransaction getToSavingsTransaction() { + return this.toSavingsTransaction; + } + + public void reverse() { + this.reversed = true; + } + + public void updateToLoanTransaction(LoanTransaction toLoanTransaction) { + this.toLoanTransaction = toLoanTransaction; + } + + public AccountTransferDetails accountTransferDetails() { + return this.accountTransferDetails; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferType.java new file mode 100755 index 000000000..4e67e35d4 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferType.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.account.domain; + +/** + * Enum representation of loan status states. + */ +public enum AccountTransferType { + + INVALID(0, "accountTransferType.invalid"), // + ACCOUNT_TRANSFER(1, "accountTransferType.account.transfer"), // + LOAN_REPAYMENT(2, "accountTransferType.loan.repayment"), // + CHARGE_PAYMENT(3, "accountTransferType.charge.payment"); // + + private final Integer value; + private final String code; + + public static AccountTransferType fromInt(final Integer statusValue) { + + AccountTransferType enumeration = AccountTransferType.INVALID; + switch (statusValue) { + case 1: + enumeration = AccountTransferType.ACCOUNT_TRANSFER; + break; + case 2: + enumeration = AccountTransferType.LOAN_REPAYMENT; + break; + case 3: + enumeration = AccountTransferType.CHARGE_PAYMENT; + break; + } + return enumeration; + } + + private AccountTransferType(final Integer value, final String code) { + this.value = value; + this.code = code; + } + + public boolean hasStateOf(final AccountTransferType state) { + return this.value.equals(state.getValue()); + } + + public Integer getValue() { + return this.value; + } + + public String getCode() { + return this.code; + } + + public boolean isAccountTransfer() { + return this.value.equals(AccountTransferType.ACCOUNT_TRANSFER.getValue()); + } + + public boolean isLoanRepayment() { + return this.value.equals(AccountTransferType.LOAN_REPAYMENT.getValue()); + } + + public boolean isChargePayment() { + return this.value.equals(AccountTransferType.CHARGE_PAYMENT.getValue()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/StandingInstructionAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/StandingInstructionAssembler.java new file mode 100755 index 000000000..b72a85f07 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/StandingInstructionAssembler.java @@ -0,0 +1,81 @@ +/** + * 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.account.domain; + +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.amountParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.instructionTypeParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.nameParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.priorityParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.recurrenceFrequencyParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.recurrenceIntervalParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.recurrenceOnMonthDayParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.recurrenceTypeParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.statusParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.validFromParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.validTillParamName; + +import java.math.BigDecimal; + +import org.joda.time.LocalDate; +import org.joda.time.MonthDay; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.organisation.monetary.domain.Money; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class StandingInstructionAssembler { + + private final AccountTransferDetailAssembler accountTransferDetailAssembler; + + @Autowired + public StandingInstructionAssembler(final AccountTransferDetailAssembler accountTransferDetailAssembler) { + + this.accountTransferDetailAssembler = accountTransferDetailAssembler; + } + + public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command) { + final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToSavingsTransfer(command); + assembleStandingInstruction(command, accountTransferDetails); + return accountTransferDetails; + } + + public void assembleStandingInstruction(final JsonCommand command, final AccountTransferDetails accountTransferDetails) { + final LocalDate validFrom = command.localDateValueOfParameterNamed(validFromParamName); + final LocalDate validTill = command.localDateValueOfParameterNamed(validTillParamName); + BigDecimal amount = null; + final BigDecimal transferAmount = command.bigDecimalValueOfParameterNamed(amountParamName); + if (transferAmount != null) { + final Money monetaryAmount = Money.of(accountTransferDetails.fromSavingsAccount().getCurrency(), transferAmount); + amount = monetaryAmount.getAmount(); + } + final Integer status = command.integerValueOfParameterNamed(statusParamName); + final Integer priority = command.integerValueOfParameterNamed(priorityParamName); + final Integer standingInstructionType = command.integerValueOfParameterNamed(instructionTypeParamName); + final Integer recurrenceType = command.integerValueOfParameterNamed(recurrenceTypeParamName); + final Integer recurrenceFrequency = command.integerValueOfParameterNamed(recurrenceFrequencyParamName); + final MonthDay recurrenceOnMonthDay = command.extractMonthDayNamed(recurrenceOnMonthDayParamName); + final Integer recurrenceInterval = command.integerValueOfParameterNamed(recurrenceIntervalParamName); + final String name = command.stringValueOfParameterNamed(nameParamName); + AccountTransferStandingInstruction accountTransferStandingInstruction = AccountTransferStandingInstruction.create( + accountTransferDetails, name, priority, standingInstructionType, status, amount, validFrom, validTill, recurrenceType, + recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay); + accountTransferDetails.updateAccountTransferStandingInstruction(accountTransferStandingInstruction); + } + + public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command) { + final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToLoanTransfer(command); + assembleStandingInstruction(command, accountTransferDetails); + return accountTransferDetails; + } + + public AccountTransferDetails assembleLoanToSavingsTransfer(final JsonCommand command) { + final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleLoanToSavingsTransfer(command); + assembleStandingInstruction(command, accountTransferDetails); + return accountTransferDetails; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/StandingInstructionPriority.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/StandingInstructionPriority.java new file mode 100755 index 000000000..7cc066541 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/StandingInstructionPriority.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.account.domain; + +/** + * Enum representation of loan status states. + */ +public enum StandingInstructionPriority { + + INVALID(0, "standingInstructionPriority.invalid"), // + URGENT(1, "standingInstructionPriority.urgent"), // + HIGH(2, "standingInstructionPriority.high"), // + MEDIUM(3, "standingInstructionPriority.medium"), // + LOW(4, "standingInstructionPriority.low"); + + private final Integer value; + private final String code; + + public static StandingInstructionPriority fromInt(final Integer statusValue) { + + StandingInstructionPriority enumeration = StandingInstructionPriority.INVALID; + switch (statusValue) { + case 1: + enumeration = StandingInstructionPriority.URGENT; + break; + case 2: + enumeration = StandingInstructionPriority.HIGH; + break; + case 3: + enumeration = StandingInstructionPriority.MEDIUM; + break; + case 4: + enumeration = StandingInstructionPriority.LOW; + break; + } + return enumeration; + } + + private StandingInstructionPriority(final Integer value, final String code) { + this.value = value; + this.code = code; + } + + public boolean hasStateOf(final StandingInstructionPriority state) { + return this.value.equals(state.getValue()); + } + + public Integer getValue() { + return this.value; + } + + public String getCode() { + return this.code; + } + + public boolean isHighPriority() { + return this.value.equals(StandingInstructionPriority.HIGH.getValue()); + } + + public boolean isLowPriority() { + return this.value.equals(StandingInstructionPriority.LOW.getValue()); + } + + public boolean isUrgentPriority() { + return this.value.equals(StandingInstructionPriority.URGENT.getValue()); + } + + public boolean isMediumPriority() { + return this.value.equals(StandingInstructionPriority.MEDIUM.getValue()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/StandingInstructionRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/StandingInstructionRepository.java new file mode 100755 index 000000000..61c544418 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/StandingInstructionRepository.java @@ -0,0 +1,12 @@ +/** + * 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.account.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface StandingInstructionRepository extends JpaRepository, + JpaSpecificationExecutor {} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/StandingInstructionStatus.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/StandingInstructionStatus.java new file mode 100755 index 000000000..6f99990df --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/StandingInstructionStatus.java @@ -0,0 +1,67 @@ +/** + * 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.account.domain; + +/** + * Enum representation of loan status states. + */ +public enum StandingInstructionStatus { + + INVALID(0, "standingInstructionStatus.invalid"), // + ACTIVE(1, "standingInstructionStatus.active"), // + DISABLED(2, "standingInstructionStatus.disabled"), // + DELETED(3, "standingInstructionStatus.deleted"); + + private final Integer value; + private final String code; + + public static StandingInstructionStatus fromInt(final Integer statusValue) { + + StandingInstructionStatus enumeration = StandingInstructionStatus.INVALID; + switch (statusValue) { + case 1: + enumeration = StandingInstructionStatus.ACTIVE; + break; + case 2: + enumeration = StandingInstructionStatus.DISABLED; + break; + case 3: + enumeration = StandingInstructionStatus.DELETED; + break; + } + return enumeration; + } + + private StandingInstructionStatus(final Integer value, final String code) { + this.value = value; + this.code = code; + } + + public boolean hasStateOf(final StandingInstructionStatus state) { + return this.value.equals(state.getValue()); + } + + public Integer getValue() { + return this.value; + } + + public String getCode() { + return this.code; + } + + public boolean isDisabled() { + return this.value.equals(StandingInstructionStatus.DISABLED.getValue()); + } + + public boolean isDeleted() { + return this.value.equals(StandingInstructionStatus.DELETED.getValue()); + } + + public boolean isActive() { + return this.value.equals(StandingInstructionStatus.ACTIVE.getValue()); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/StandingInstructionType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/StandingInstructionType.java new file mode 100755 index 000000000..b3476905a --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/StandingInstructionType.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.account.domain; + +/** + * Enum representation of loan status states. + */ +public enum StandingInstructionType { + + INVALID(0, "standingInstructionType.invalid"), // + FIXED(1, "standingInstructionType.fixed"), // + DUES(2, "standingInstructionType.dues"); // + + private final Integer value; + private final String code; + + public static StandingInstructionType fromInt(final Integer statusValue) { + + StandingInstructionType enumeration = StandingInstructionType.INVALID; + switch (statusValue) { + case 1: + enumeration = StandingInstructionType.FIXED; + break; + case 2: + enumeration = StandingInstructionType.DUES; + break; + } + return enumeration; + } + + private StandingInstructionType(final Integer value, final String code) { + this.value = value; + this.code = code; + } + + public boolean hasStateOf(final StandingInstructionType state) { + return this.value.equals(state.getValue()); + } + + public Integer getValue() { + return this.value; + } + + public String getCode() { + return this.code; + } + + public boolean isFixedAmoutTransfer() { + return this.value.equals(StandingInstructionType.FIXED.getValue()); + } + + public boolean isDuesAmoutTransfer() { + return this.value.equals(StandingInstructionType.DUES.getValue()); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/exception/StandingInstructionNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/exception/StandingInstructionNotFoundException.java new file mode 100755 index 000000000..582d60ec8 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/exception/StandingInstructionNotFoundException.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.account.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +/** + * A {@link RuntimeException} thrown when loan resources are not found. + */ +public class StandingInstructionNotFoundException extends AbstractPlatformResourceNotFoundException { + + public StandingInstructionNotFoundException(final Long id) { + super("error.msg.standinginstruction.id.invalid", "AccountTransferStandingInstruction with identifier " + id + " does not exist", + id); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/handler/CreateStandingInstructionCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/handler/CreateStandingInstructionCommandHandler.java new file mode 100755 index 000000000..d2a612b7f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/handler/CreateStandingInstructionCommandHandler.java @@ -0,0 +1,25 @@ +package org.mifosplatform.portfolio.account.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.account.service.StandingInstructionWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class CreateStandingInstructionCommandHandler implements NewCommandSourceHandler { + + private StandingInstructionWritePlatformService standingInstructionWritePlatformService; + + @Autowired + public CreateStandingInstructionCommandHandler(StandingInstructionWritePlatformService standingInstructionWritePlatformService) { + this.standingInstructionWritePlatformService = standingInstructionWritePlatformService; + } + + @Override + public CommandProcessingResult processCommand(JsonCommand command) { + return this.standingInstructionWritePlatformService.create(command); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/handler/DeleteStandingInstructionCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/handler/DeleteStandingInstructionCommandHandler.java new file mode 100755 index 000000000..e8add4957 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/handler/DeleteStandingInstructionCommandHandler.java @@ -0,0 +1,25 @@ +package org.mifosplatform.portfolio.account.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.account.service.StandingInstructionWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class DeleteStandingInstructionCommandHandler implements NewCommandSourceHandler { + + private StandingInstructionWritePlatformService standingInstructionWritePlatformService; + + @Autowired + public DeleteStandingInstructionCommandHandler(StandingInstructionWritePlatformService standingInstructionWritePlatformService) { + this.standingInstructionWritePlatformService = standingInstructionWritePlatformService; + } + + @Override + public CommandProcessingResult processCommand(JsonCommand command) { + return this.standingInstructionWritePlatformService.delete(command.entityId()); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/handler/UpdateStandingInstructionCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/handler/UpdateStandingInstructionCommandHandler.java new file mode 100755 index 000000000..7ae9ebf9d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/handler/UpdateStandingInstructionCommandHandler.java @@ -0,0 +1,25 @@ +package org.mifosplatform.portfolio.account.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.account.service.StandingInstructionWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class UpdateStandingInstructionCommandHandler implements NewCommandSourceHandler { + + private StandingInstructionWritePlatformService standingInstructionWritePlatformService; + + @Autowired + public UpdateStandingInstructionCommandHandler(StandingInstructionWritePlatformService standingInstructionWritePlatformService) { + this.standingInstructionWritePlatformService = standingInstructionWritePlatformService; + } + + @Override + public CommandProcessingResult processCommand(JsonCommand command) { + return this.standingInstructionWritePlatformService.update(command.entityId(), command); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransferEnumerations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransferEnumerations.java index 5aac38d38..149d001db 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransferEnumerations.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransferEnumerations.java @@ -7,6 +7,11 @@ package org.mifosplatform.portfolio.account.service; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.portfolio.account.PortfolioAccountType; +import org.mifosplatform.portfolio.account.domain.AccountTransferRecurrenceType; +import org.mifosplatform.portfolio.account.domain.AccountTransferType; +import org.mifosplatform.portfolio.account.domain.StandingInstructionPriority; +import org.mifosplatform.portfolio.account.domain.StandingInstructionStatus; +import org.mifosplatform.portfolio.account.domain.StandingInstructionType; public class AccountTransferEnumerations { @@ -35,4 +40,137 @@ public class AccountTransferEnumerations { return optionData; } + + public static EnumOptionData recurrenceType(final Integer type) { + return recurrenceType(AccountTransferRecurrenceType.fromInt(type)); + } + + public static EnumOptionData recurrenceType(final AccountTransferRecurrenceType type) { + EnumOptionData optionData = null; + if (type != null) { + switch (type) { + case INVALID: + break; + case PERIODIC: + optionData = new EnumOptionData(AccountTransferRecurrenceType.PERIODIC.getValue().longValue(), + AccountTransferRecurrenceType.PERIODIC.getCode(), "Periodic Recurrence"); + break; + case AS_PER_DUES: + optionData = new EnumOptionData(AccountTransferRecurrenceType.AS_PER_DUES.getValue().longValue(), + AccountTransferRecurrenceType.AS_PER_DUES.getCode(), "As Per Dues Recurrence"); + break; + } + } + return optionData; + } + + public static EnumOptionData transferType(final Integer type) { + return transferType(AccountTransferType.fromInt(type)); + } + + public static EnumOptionData transferType(final AccountTransferType type) { + EnumOptionData optionData = null; + if (type != null) { + switch (type) { + case INVALID: + break; + case ACCOUNT_TRANSFER: + optionData = new EnumOptionData(AccountTransferType.ACCOUNT_TRANSFER.getValue().longValue(), + AccountTransferType.ACCOUNT_TRANSFER.getCode(), "Account Transfer"); + break; + case LOAN_REPAYMENT: + optionData = new EnumOptionData(AccountTransferType.LOAN_REPAYMENT.getValue().longValue(), + AccountTransferType.LOAN_REPAYMENT.getCode(), "Loan Repayment"); + break; + case CHARGE_PAYMENT: + optionData = new EnumOptionData(AccountTransferType.CHARGE_PAYMENT.getValue().longValue(), + AccountTransferType.CHARGE_PAYMENT.getCode(), "Charge Payment"); + break; + } + } + return optionData; + } + + public static EnumOptionData standingInstructionPriority(final Integer type) { + return standingInstructionPriority(StandingInstructionPriority.fromInt(type)); + } + + public static EnumOptionData standingInstructionPriority(final StandingInstructionPriority type) { + EnumOptionData optionData = null; + if (type != null) { + switch (type) { + case INVALID: + break; + case HIGH: + optionData = new EnumOptionData(StandingInstructionPriority.HIGH.getValue().longValue(), + StandingInstructionPriority.HIGH.getCode(), "High Priority"); + break; + case LOW: + optionData = new EnumOptionData(StandingInstructionPriority.LOW.getValue().longValue(), + StandingInstructionPriority.LOW.getCode(), "Low Priority"); + break; + case MEDIUM: + optionData = new EnumOptionData(StandingInstructionPriority.MEDIUM.getValue().longValue(), + StandingInstructionPriority.MEDIUM.getCode(), "Medium Priority"); + break; + case URGENT: + optionData = new EnumOptionData(StandingInstructionPriority.URGENT.getValue().longValue(), + StandingInstructionPriority.URGENT.getCode(), "Urgent Priority"); + break; + } + } + return optionData; + } + + public static EnumOptionData standingInstructionStatus(final Integer type) { + return standingInstructionStatus(StandingInstructionStatus.fromInt(type)); + } + + public static EnumOptionData standingInstructionStatus(final StandingInstructionStatus type) { + EnumOptionData optionData = null; + if (type != null) { + switch (type) { + case INVALID: + break; + case ACTIVE: + optionData = new EnumOptionData(StandingInstructionStatus.ACTIVE.getValue().longValue(), + StandingInstructionStatus.ACTIVE.getCode(), "Active"); + break; + case DELETED: + optionData = new EnumOptionData(StandingInstructionStatus.DELETED.getValue().longValue(), + StandingInstructionStatus.DELETED.getCode(), "Deleted"); + break; + case DISABLED: + optionData = new EnumOptionData(StandingInstructionStatus.DISABLED.getValue().longValue(), + StandingInstructionStatus.DISABLED.getCode(), "Disabled"); + break; + } + } + return optionData; + } + + public static EnumOptionData standingInstructionType(final Integer type) { + return standingInstructionType(StandingInstructionType.fromInt(type)); + } + + public static EnumOptionData standingInstructionType(final StandingInstructionType type) { + EnumOptionData optionData = null; + if (type != null) { + switch (type) { + case INVALID: + break; + case DUES: + optionData = new EnumOptionData(StandingInstructionType.DUES.getValue().longValue(), + StandingInstructionType.DUES.getCode(), "Dues"); + break; + case FIXED: + optionData = new EnumOptionData(StandingInstructionType.FIXED.getValue().longValue(), + StandingInstructionType.FIXED.getCode(), "Fixed"); + break; + + } + } + return optionData; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformService.java index 0a5908546..31fe4ed65 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformService.java @@ -15,9 +15,11 @@ public interface AccountTransfersReadPlatformService { AccountTransferData retrieveTemplate(Long fromOfficeId, Long fromClientId, Long fromAccountId, Integer fromAccountType, Long toOfficeId, Long toClientId, Long toAccountId, Integer toAccountType); - Page retrieveAll(SearchParameters searchParameters); + Page retrieveAll(SearchParameters searchParameters, Long accountDetailId); AccountTransferData retrieveOne(Long transferId); boolean isAccountTransfer(Long transactionId, PortfolioAccountType accountType); + + Page retrieveByStandingInstruction(Long id, SearchParameters searchParameters); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java index 0982c70d7..5be3dac29 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java @@ -188,11 +188,16 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers } @Override - public Page retrieveAll(final SearchParameters searchParameters) { + public Page retrieveAll(final SearchParameters searchParameters,final Long accountDetailId) { final StringBuilder sqlBuilder = new StringBuilder(200); sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); sqlBuilder.append(this.accountTransfersMapper.schema()); + Object[] finalObjectArray = {}; + if(accountDetailId != null){ + sqlBuilder.append(" where att.account_transfer_details_id=?"); + finalObjectArray = new Object[]{ accountDetailId }; + } if (searchParameters.isOrderByRequested()) { sqlBuilder.append(" order by ").append(searchParameters.getOrderBy()); @@ -209,9 +214,6 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers } } - final Object[] objectArray = new Object[2]; - final int arrayPos = 0; - final Object[] finalObjectArray = Arrays.copyOf(objectArray, arrayPos); final String sqlCountRows = "SELECT FOUND_ROWS()"; return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), finalObjectArray, this.accountTransfersMapper); @@ -221,7 +223,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers public AccountTransferData retrieveOne(final Long transferId) { try { - final String sql = "select " + this.accountTransfersMapper.schema() + " where sat.id = ?"; + final String sql = "select " + this.accountTransfersMapper.schema() + " where att.id = ?"; return this.jdbcTemplate.queryForObject(sql, this.accountTransfersMapper, new Object[] { transferId }); } catch (final EmptyResultDataAccessException e) { @@ -235,11 +237,11 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers public AccountTransfersMapper() { final StringBuilder sqlBuilder = new StringBuilder(400); - sqlBuilder.append("sat.id as id, sat.is_reversed as isReversed,"); - sqlBuilder.append("sat.transaction_date as transferDate, sat.amount as transferAmount,"); - sqlBuilder.append("sat.description as transferDescription,"); - sqlBuilder.append("sat.currency_code as currencyCode, sat.currency_digits as currencyDigits,"); - sqlBuilder.append("sat.currency_multiplesof as inMultiplesOf, "); + sqlBuilder.append("att.id as id, att.is_reversed as isReversed,"); + sqlBuilder.append("att.transaction_date as transferDate, att.amount as transferAmount,"); + sqlBuilder.append("att.description as transferDescription,"); + sqlBuilder.append("att.currency_code as currencyCode, att.currency_digits as currencyDigits,"); + sqlBuilder.append("att.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("fromoff.id as fromOfficeId, fromoff.name as fromOfficeName,"); @@ -254,18 +256,21 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers sqlBuilder.append("fromsavtran.transaction_type_enum as fromSavingsAccountTransactionType,"); sqlBuilder.append("tosavtran.id as toSavingsAccountTransactionId,"); sqlBuilder.append("tosavtran.transaction_type_enum as toSavingsAccountTransactionType"); - sqlBuilder.append(" FROM m_savings_account_transfer sat "); - sqlBuilder.append("join m_currency curr on curr.code = sat.currency_code "); - sqlBuilder.append("join m_office fromoff on fromoff.id = sat.from_office_id "); - sqlBuilder.append("join m_office tooff on tooff.id = sat.to_office_id "); - sqlBuilder.append("join m_client fromclient on fromclient.id = sat.from_client_id "); - sqlBuilder.append("join m_client toclient on toclient.id = sat.to_client_id "); - sqlBuilder.append("left join m_savings_account fromsavacc on fromsavacc.id = sat.from_savings_account_id "); - sqlBuilder.append("left join m_loan fromloanacc on fromloanacc.id = sat.from_loan_account_id "); - sqlBuilder.append("left join m_savings_account tosavacc on tosavacc.id = sat.to_savings_account_id "); - sqlBuilder.append("left join m_loan toloanacc on toloanacc.id = sat.to_loan_account_id "); - sqlBuilder.append("left join m_savings_account_transaction fromsavtran on fromsavtran.id = sat.from_savings_transaction_id "); - sqlBuilder.append("left join m_savings_account_transaction tosavtran on tosavtran.id = sat.to_savings_transaction_id "); + sqlBuilder.append(" FROM m_account_transfer_transaction att "); + sqlBuilder.append("left join m_account_transfer_details atd on atd.id = att.account_transfer_details_id "); + sqlBuilder.append("join m_currency curr on curr.code = att.currency_code "); + sqlBuilder.append("join m_office fromoff on fromoff.id = atd.from_office_id "); + sqlBuilder.append("join m_office tooff on tooff.id = atd.to_office_id "); + sqlBuilder.append("join m_client fromclient on fromclient.id = atd.from_client_id "); + sqlBuilder.append("join m_client toclient on toclient.id = atd.to_client_id "); + sqlBuilder.append("left join m_savings_account fromsavacc on fromsavacc.id = atd.from_savings_account_id "); + sqlBuilder.append("left join m_loan fromloanacc on fromloanacc.id = atd.from_loan_account_id "); + sqlBuilder.append("left join m_savings_account tosavacc on tosavacc.id = atd.to_savings_account_id "); + sqlBuilder.append("left join m_loan toloanacc on toloanacc.id = atd.to_loan_account_id "); + sqlBuilder.append("left join m_savings_account_transaction fromsavtran on fromsavtran.id = att.from_savings_transaction_id "); + sqlBuilder.append("left join m_savings_account_transaction tosavtran on tosavtran.id = att.to_savings_transaction_id "); + sqlBuilder.append("left join m_loan_transaction fromloantran on fromloantran.id = att.from_savings_transaction_id "); + sqlBuilder.append("left join m_loan_transaction toloantran on toloantran.id = att.to_savings_transaction_id "); this.schemaSql = sqlBuilder.toString(); } @@ -345,7 +350,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers @Override public boolean isAccountTransfer(final Long transactionId, final PortfolioAccountType accountType) { - final StringBuilder sql = new StringBuilder("select count(*) from m_savings_account_transfer at where "); + final StringBuilder sql = new StringBuilder("select count(*) from m_account_transfer_transaction at where "); if (accountType.isLoanAccount()) { sql.append("at.from_loan_transaction_id=").append(transactionId).append(" or at.to_loan_transaction_id=").append(transactionId); } else { @@ -357,4 +362,37 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers return count > 0; } + @Override + public Page retrieveByStandingInstruction(final Long id, final SearchParameters searchParameters) { + + final StringBuilder sqlBuilder = new StringBuilder(200); + sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder + .append(this.accountTransfersMapper.schema()) + .append(" join m_account_transfer_standing_instructions atsi on atsi.account_transfer_details_id = att.account_transfer_details_id "); + sqlBuilder.append(" where atsi.id = ?"); + + if (searchParameters != null) { + if (searchParameters.isOrderByRequested()) { + sqlBuilder.append(" order by ").append(searchParameters.getOrderBy()); + + if (searchParameters.isSortOrderProvided()) { + sqlBuilder.append(' ').append(searchParameters.getSortOrder()); + } + } + + if (searchParameters.isLimited()) { + sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + if (searchParameters.isOffset()) { + sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + } + } + } + + final Object[] finalObjectArray = { id }; + final String sqlCountRows = "SELECT FOUND_ROWS()"; + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), finalObjectArray, + this.accountTransfersMapper); + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java index 6fcd86d22..eeed14fde 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java @@ -5,12 +5,12 @@ */ package org.mifosplatform.portfolio.account.service; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromAccountIdParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromAccountTypeParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toAccountIdParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toAccountTypeParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferAmountParamName; -import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferDateParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.fromAccountIdParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.fromAccountTypeParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.toAccountIdParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.toAccountTypeParamName; +import static org.mifosplatform.portfolio.account.api.AccountTransfersApiConstants.transferAmountParamName; +import static org.mifosplatform.portfolio.account.api.AccountTransfersApiConstants.transferDateParamName; import java.math.BigDecimal; import java.util.List; @@ -25,9 +25,12 @@ import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder import org.mifosplatform.portfolio.account.PortfolioAccountType; import org.mifosplatform.portfolio.account.data.AccountTransferDTO; import org.mifosplatform.portfolio.account.data.AccountTransfersDataValidator; -import org.mifosplatform.portfolio.account.domain.AccountTransfer; import org.mifosplatform.portfolio.account.domain.AccountTransferAssembler; +import org.mifosplatform.portfolio.account.domain.AccountTransferDetailRepository; +import org.mifosplatform.portfolio.account.domain.AccountTransferDetails; import org.mifosplatform.portfolio.account.domain.AccountTransferRepository; +import org.mifosplatform.portfolio.account.domain.AccountTransferTransaction; +import org.mifosplatform.portfolio.account.domain.AccountTransferType; import org.mifosplatform.portfolio.loanaccount.domain.Loan; import org.mifosplatform.portfolio.loanaccount.domain.LoanAccountDomainService; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; @@ -53,13 +56,15 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer private final LoanAssembler loanAccountAssembler; private final LoanAccountDomainService loanAccountDomainService; private final SavingsAccountWritePlatformService savingsAccountWritePlatformService; + private final AccountTransferDetailRepository accountTransferDetailRepository; @Autowired public AccountTransfersWritePlatformServiceImpl(final AccountTransfersDataValidator accountTransfersDataValidator, final AccountTransferAssembler accountTransferAssembler, final AccountTransferRepository accountTransferRepository, final SavingsAccountAssembler savingsAccountAssembler, final SavingsAccountDomainService savingsAccountDomainService, final LoanAssembler loanAssembler, final LoanAccountDomainService loanAccountDomainService, - final SavingsAccountWritePlatformService savingsAccountWritePlatformService) { + final SavingsAccountWritePlatformService savingsAccountWritePlatformService, + final AccountTransferDetailRepository accountTransferDetailRepository) { this.accountTransfersDataValidator = accountTransfersDataValidator; this.accountTransferAssembler = accountTransferAssembler; this.accountTransferRepository = accountTransferRepository; @@ -68,6 +73,7 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer this.loanAccountAssembler = loanAssembler; this.loanAccountDomainService = loanAccountDomainService; this.savingsAccountWritePlatformService = savingsAccountWritePlatformService; + this.accountTransferDetailRepository = accountTransferDetailRepository; } @Transactional @@ -90,7 +96,7 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer final PaymentDetail paymentDetail = null; Long fromSavingsAccountId = null; - Long transferTransactionId = null; + Long transferDetailId = null; if (isSavingsToSavingsAccountTransfer(fromAccountType, toAccountType)) { fromSavingsAccountId = command.longValueOfParameterNamed(fromAccountIdParamName); @@ -105,10 +111,10 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, fmt, transactionDate, transactionAmount, paymentDetail); - final AccountTransfer transferTransaction = this.accountTransferAssembler.assembleSavingsToSavingsTransfer(command, withdrawal, - deposit); - this.accountTransferRepository.saveAndFlush(transferTransaction); - transferTransactionId = transferTransaction.getId(); + final AccountTransferDetails accountTransferDetails = this.accountTransferAssembler.assembleSavingsToSavingsTransfer(command, + fromSavingsAccount, toSavingsAccount, withdrawal, deposit); + this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); + transferDetailId = accountTransferDetails.getId(); } else if (isSavingsToLoanAccountTransfer(fromAccountType, toAccountType)) { // @@ -124,10 +130,10 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer final LoanTransaction loanRepaymentTransaction = this.loanAccountDomainService.makeRepayment(toLoanAccount, new CommandProcessingResultBuilder(), transactionDate, transactionAmount, paymentDetail, null, null); - final AccountTransfer transferTransaction = this.accountTransferAssembler.assembleSavingsToLoanTransfer(command, + final AccountTransferDetails accountTransferDetails = this.accountTransferAssembler.assembleSavingsToLoanTransfer(command, fromSavingsAccount, toLoanAccount, withdrawal, loanRepaymentTransaction); - this.accountTransferRepository.saveAndFlush(transferTransaction); - transferTransactionId = transferTransaction.getId(); + this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); + transferDetailId = accountTransferDetails.getId(); } else if (isLoanToSavingsAccountTransfer(fromAccountType, toAccountType)) { // FIXME - kw - ADD overpaid loan to savings account transfer @@ -146,16 +152,16 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, fmt, transactionDate, transactionAmount, paymentDetail); - final AccountTransfer transferTransaction = this.accountTransferAssembler.assembleLoanToSavingsTransfer(command, + final AccountTransferDetails accountTransferDetails = this.accountTransferAssembler.assembleLoanToSavingsTransfer(command, fromLoanAccount, toSavingsAccount, deposit, loanRefundTransaction); - this.accountTransferRepository.saveAndFlush(transferTransaction); - transferTransactionId = transferTransaction.getId(); + this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); + transferDetailId = accountTransferDetails.getId(); } else { } - final CommandProcessingResultBuilder builder = new CommandProcessingResultBuilder().withEntityId(transferTransactionId); + final CommandProcessingResultBuilder builder = new CommandProcessingResultBuilder().withEntityId(transferDetailId); if (fromAccountType.isSavingsAccount()) { @@ -168,7 +174,7 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer @Override @Transactional public void reverseTransfersWithFromAccountType(final Long accountNumber, final PortfolioAccountType accountTypeId) { - List acccountTransfers = null; + List acccountTransfers = null; if (accountTypeId.isLoanAccount()) { acccountTransfers = this.accountTransferRepository.findByFromLoanId(accountNumber); } @@ -181,7 +187,7 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer @Override @Transactional public void reverseAllTransactions(final Long accountId, final PortfolioAccountType accountTypeId) { - List acccountTransfers = null; + List acccountTransfers = null; if (accountTypeId.isLoanAccount()) { acccountTransfers = this.accountTransferRepository.findAllByLoanId(accountId); } @@ -193,20 +199,21 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer /** * @param acccountTransfers */ - private void undoTransactions(final List acccountTransfers) { - for (final AccountTransfer accountTransfer : acccountTransfers) { + private void undoTransactions(final List acccountTransfers) { + for (final AccountTransferTransaction accountTransfer : acccountTransfers) { if (accountTransfer.getFromLoanTransaction() != null) { this.loanAccountDomainService.reverseTransfer(accountTransfer.getFromLoanTransaction()); } else if (accountTransfer.getToLoanTransaction() != null) { this.loanAccountDomainService.reverseTransfer(accountTransfer.getToLoanTransaction()); } if (accountTransfer.getFromTransaction() != null) { - this.savingsAccountWritePlatformService.undoTransaction(accountTransfer.getFromAccount().getId(), accountTransfer - .getFromTransaction().getId(), true); + this.savingsAccountWritePlatformService.undoTransaction(accountTransfer.accountTransferDetails().fromSavingsAccount() + .getId(), accountTransfer.getFromTransaction().getId(), true); } if (accountTransfer.getToSavingsTransaction() != null) { - this.savingsAccountWritePlatformService.undoTransaction(accountTransfer.getToSavingsAccount().getId(), accountTransfer - .getToSavingsTransaction().getId(), true); + this.savingsAccountWritePlatformService.undoTransaction( + accountTransfer.accountTransferDetails().toSavingsAccount().getId(), accountTransfer.getToSavingsTransaction() + .getId(), true); } accountTransfer.reverse(); this.accountTransferRepository.save(accountTransfer); @@ -217,25 +224,93 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer @Transactional public Long transferFunds(final AccountTransferDTO accountTransferDTO) { Long transferTransactionId = null; + AccountTransferDetails accountTransferDetails = accountTransferDTO.getAccountTransferDetails(); if (isSavingsToLoanAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) { // - final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId()); + SavingsAccount fromSavingsAccount = null; + Loan toLoanAccount = null; + if (accountTransferDetails == null) { + fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId()); + toLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId()); + } else { + fromSavingsAccount = accountTransferDetails.fromSavingsAccount(); + this.savingsAccountAssembler.setHelpers(fromSavingsAccount); + toLoanAccount = accountTransferDetails.toLoanAccount(); + this.loanAccountAssembler.setHelpers(toLoanAccount); + } final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(), fromSavingsAccount.isWithdrawalFeeApplicableForTransfer()); - final Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId()); + LoanTransaction loanTransaction = null; - final LoanTransaction loanRepaymentTransaction = this.loanAccountDomainService.makeChargePayment(toLoanAccount, - accountTransferDTO.getChargeId(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), - accountTransferDTO.getPaymentDetail(), null, null, accountTransferDTO.getToTransferType(), - accountTransferDTO.getLoanInstallmentNumber()); + if (AccountTransferType.fromInt(accountTransferDTO.getTransferType()).isChargePayment()) { + loanTransaction = this.loanAccountDomainService.makeChargePayment(toLoanAccount, accountTransferDTO.getChargeId(), + accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), + accountTransferDTO.getPaymentDetail(), null, null, accountTransferDTO.getToTransferType(), + accountTransferDTO.getLoanInstallmentNumber()); - final AccountTransfer transferTransaction = this.accountTransferAssembler.assembleSavingsToLoanTransfer(accountTransferDTO, - fromSavingsAccount, toLoanAccount, withdrawal, loanRepaymentTransaction); - this.accountTransferRepository.saveAndFlush(transferTransaction); - transferTransactionId = transferTransaction.getId(); + } else { + loanTransaction = this.loanAccountDomainService.makeRepayment(toLoanAccount, new CommandProcessingResultBuilder(), + accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), + accountTransferDTO.getPaymentDetail(), null, null); + } + + accountTransferDetails = this.accountTransferAssembler.assembleSavingsToLoanTransfer(accountTransferDTO, fromSavingsAccount, + toLoanAccount, withdrawal, loanTransaction); + this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); + transferTransactionId = accountTransferDetails.getId(); + } else if (isSavingsToSavingsAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) { + + SavingsAccount fromSavingsAccount = null; + SavingsAccount toSavingsAccount = null; + if (accountTransferDetails == null) { + fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId()); + toSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId()); + } else { + fromSavingsAccount = accountTransferDetails.fromSavingsAccount(); + this.savingsAccountAssembler.setHelpers(fromSavingsAccount); + toSavingsAccount = accountTransferDetails.toSavingsAccount(); + this.savingsAccountAssembler.setHelpers(toSavingsAccount); + } + final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, + accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), + accountTransferDTO.getPaymentDetail(), fromSavingsAccount.isWithdrawalFeeApplicableForTransfer()); + + final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, + accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), + accountTransferDTO.getPaymentDetail()); + + accountTransferDetails = this.accountTransferAssembler.assembleSavingsToSavingsTransfer(accountTransferDTO, fromSavingsAccount, + toSavingsAccount, withdrawal, deposit); + this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); + transferTransactionId = accountTransferDetails.getId(); + + } else if (isLoanToSavingsAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) { + + Loan fromLoanAccount = null; + SavingsAccount toSavingsAccount = null; + if (accountTransferDetails == null) { + fromLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId()); + toSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId()); + } else { + fromLoanAccount = accountTransferDetails.fromLoanAccount(); + this.loanAccountAssembler.setHelpers(fromLoanAccount); + toSavingsAccount = accountTransferDetails.toSavingsAccount(); + this.savingsAccountAssembler.setHelpers(toSavingsAccount); + } + final LoanTransaction loanRefundTransaction = this.loanAccountDomainService.makeRefund(accountTransferDTO.getFromAccountId(), + new CommandProcessingResultBuilder(), accountTransferDTO.getTransactionDate(), + accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(), null, null); + + final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, + accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), + accountTransferDTO.getPaymentDetail()); + accountTransferDetails = this.accountTransferAssembler.assembleLoanToSavingsTransfer(accountTransferDTO, fromLoanAccount, + toSavingsAccount, deposit, loanRefundTransaction); + this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); + transferTransactionId = accountTransferDetails.getId(); } return transferTransactionId; @@ -244,7 +319,7 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer @Override @Transactional public void updateLoanTransaction(final Long loanTransactionId, final LoanTransaction newLoanTransaction) { - final AccountTransfer transferTransaction = this.accountTransferRepository.findByToLoanTransactionId(loanTransactionId); + final AccountTransferTransaction transferTransaction = this.accountTransferRepository.findByToLoanTransactionId(loanTransactionId); if (transferTransaction != null) { transferTransaction.updateToLoanTransaction(newLoanTransaction); this.accountTransferRepository.save(transferTransaction); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/StandingInstructionReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/StandingInstructionReadPlatformService.java new file mode 100755 index 000000000..304ddf8da --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/StandingInstructionReadPlatformService.java @@ -0,0 +1,28 @@ +/** + * 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.account.service; + +import java.util.Collection; + +import org.mifosplatform.infrastructure.core.service.Page; +import org.mifosplatform.portfolio.account.data.StandingInstructionDTO; +import org.mifosplatform.portfolio.account.data.StandingInstructionData; +import org.mifosplatform.portfolio.account.data.StandingInstructionDuesData; + +public interface StandingInstructionReadPlatformService { + + StandingInstructionData retrieveTemplate(Long fromOfficeId, Long fromClientId, Long fromAccountId, Integer fromAccountType, + Long toOfficeId, Long toClientId, Long toAccountId, Integer toAccountType, Integer transferType); + + Page retrieveAll(StandingInstructionDTO standingInstructionDTO); + + StandingInstructionData retrieveOne(Long instructionId); + + Collection retrieveAll(Integer status); + + StandingInstructionDuesData retriveLoanDuesData(Long loanId); + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/StandingInstructionReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/StandingInstructionReadPlatformServiceImpl.java new file mode 100755 index 000000000..c8eabfe8d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/StandingInstructionReadPlatformServiceImpl.java @@ -0,0 +1,575 @@ +/** + * 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.account.service; + +import static org.mifosplatform.portfolio.account.service.AccountTransferEnumerations.accountType; +import static org.mifosplatform.portfolio.account.service.AccountTransferEnumerations.recurrenceType; +import static org.mifosplatform.portfolio.account.service.AccountTransferEnumerations.standingInstructionPriority; +import static org.mifosplatform.portfolio.account.service.AccountTransferEnumerations.standingInstructionStatus; +import static org.mifosplatform.portfolio.account.service.AccountTransferEnumerations.standingInstructionType; +import static org.mifosplatform.portfolio.account.service.AccountTransferEnumerations.transferType; + +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.joda.time.LocalDate; +import org.joda.time.MonthDay; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +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.organisation.office.data.OfficeData; +import org.mifosplatform.organisation.office.service.OfficeReadPlatformService; +import org.mifosplatform.portfolio.account.PortfolioAccountType; +import org.mifosplatform.portfolio.account.data.PortfolioAccountData; +import org.mifosplatform.portfolio.account.data.StandingInstructionDTO; +import org.mifosplatform.portfolio.account.data.StandingInstructionData; +import org.mifosplatform.portfolio.account.data.StandingInstructionDuesData; +import org.mifosplatform.portfolio.account.domain.AccountTransferRecurrenceType; +import org.mifosplatform.portfolio.account.domain.AccountTransferType; +import org.mifosplatform.portfolio.account.domain.StandingInstructionPriority; +import org.mifosplatform.portfolio.account.domain.StandingInstructionStatus; +import org.mifosplatform.portfolio.account.domain.StandingInstructionType; +import org.mifosplatform.portfolio.account.exception.AccountTransferNotFoundException; +import org.mifosplatform.portfolio.client.data.ClientData; +import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; +import org.mifosplatform.portfolio.common.service.CommonEnumerations; +import org.mifosplatform.portfolio.common.service.DropdownReadPlatformService; +import org.mifosplatform.portfolio.group.service.SearchParameters; +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 StandingInstructionReadPlatformServiceImpl implements StandingInstructionReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + private final ClientReadPlatformService clientReadPlatformService; + private final OfficeReadPlatformService officeReadPlatformService; + private final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService; + private final DropdownReadPlatformService dropdownReadPlatformService; + + // mapper + private final StandingInstructionMapper standingInstructionMapper; + + // pagination + private final PaginationHelper paginationHelper = new PaginationHelper(); + + @Autowired + public StandingInstructionReadPlatformServiceImpl(final RoutingDataSource dataSource, + final ClientReadPlatformService clientReadPlatformService, final OfficeReadPlatformService officeReadPlatformService, + final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService, + final DropdownReadPlatformService dropdownReadPlatformService) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.clientReadPlatformService = clientReadPlatformService; + this.officeReadPlatformService = officeReadPlatformService; + this.portfolioAccountReadPlatformService = portfolioAccountReadPlatformService; + this.dropdownReadPlatformService = dropdownReadPlatformService; + this.standingInstructionMapper = new StandingInstructionMapper(); + } + + @Override + public StandingInstructionData retrieveTemplate(final Long fromOfficeId, final Long fromClientId, final Long fromAccountId, + final Integer fromAccountType, final Long toOfficeId, final Long toClientId, final Long toAccountId, + final Integer toAccountType, Integer transferType) { + + AccountTransferType accountTransferType = AccountTransferType.INVALID; + if (transferType != null) { + accountTransferType = AccountTransferType.fromInt(transferType); + } + + final EnumOptionData loanAccountType = accountType(PortfolioAccountType.LOAN); + final EnumOptionData savingsAccountType = accountType(PortfolioAccountType.SAVINGS); + + final Integer mostRelevantFromAccountType = fromAccountType; + Collection fromAccountTypeOptions = null; + Collection toAccountTypeOptions = null; + + if (accountTransferType.isAccountTransfer()) { + fromAccountTypeOptions = Arrays.asList(savingsAccountType); + toAccountTypeOptions = Arrays.asList(savingsAccountType); + } else if (accountTransferType.isLoanRepayment()) { + fromAccountTypeOptions = Arrays.asList(savingsAccountType); + toAccountTypeOptions = Arrays.asList(loanAccountType); + } else { + fromAccountTypeOptions = Arrays.asList(savingsAccountType, loanAccountType); + toAccountTypeOptions = Arrays.asList(loanAccountType, savingsAccountType); + } + final Integer mostRelevantToAccountType = toAccountType; + + final EnumOptionData fromAccountTypeData = accountType(mostRelevantFromAccountType); + final EnumOptionData toAccountTypeData = accountType(mostRelevantToAccountType); + + // from settings + OfficeData fromOffice = null; + ClientData fromClient = null; + PortfolioAccountData fromAccount = null; + + OfficeData toOffice = null; + ClientData toClient = null; + PortfolioAccountData toAccount = null; + + // template + Collection fromAccountOptions = null; + Collection toAccountOptions = null; + + Long mostRelevantFromOfficeId = fromOfficeId; + Long mostRelevantFromClientId = fromClientId; + + Long mostRelevantToOfficeId = toOfficeId; + Long mostRelevantToClientId = toClientId; + + if (fromAccountId != null) { + Integer accountType; + if (mostRelevantFromAccountType == 1) { + accountType = PortfolioAccountType.LOAN.getValue(); + } else { + accountType = PortfolioAccountType.SAVINGS.getValue(); + } + fromAccount = this.portfolioAccountReadPlatformService.retrieveOne(fromAccountId, accountType); + + // override provided fromClient with client of account + mostRelevantFromClientId = fromAccount.clientId(); + } + + if (mostRelevantFromClientId != null) { + fromClient = this.clientReadPlatformService.retrieveOne(mostRelevantFromClientId); + mostRelevantFromOfficeId = fromClient.officeId(); + long[] loanStatus = null; + if (mostRelevantFromAccountType == 1) { + loanStatus = new long[] { 300, 700 }; + } + fromAccountOptions = this.portfolioAccountReadPlatformService.retrieveAllForLookup(mostRelevantFromAccountType, + mostRelevantFromClientId, loanStatus); + } + + Collection fromOfficeOptions = this.officeReadPlatformService.retrieveAllOfficesForDropdown(); + Collection fromClientOptions = null; + + if (mostRelevantFromOfficeId != null) { + fromOffice = this.officeReadPlatformService.retrieveOffice(mostRelevantFromOfficeId); + fromClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantFromOfficeId); + } + + // defaults + final LocalDate transferDate = DateUtils.getLocalDateOfTenant(); + Collection toOfficeOptions = fromOfficeOptions; + Collection toClientOptions = null; + + if (toAccountId != null && fromAccount != null) { + toAccount = this.portfolioAccountReadPlatformService.retrieveOne(toAccountId, mostRelevantToAccountType, + fromAccount.currencyCode()); + mostRelevantToClientId = toAccount.clientId(); + } + + if (mostRelevantToClientId != null) { + toClient = this.clientReadPlatformService.retrieveOne(mostRelevantToClientId); + mostRelevantToOfficeId = toClient.officeId(); + + toClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantToOfficeId); + + toAccountOptions = retrieveToAccounts(fromAccount, mostRelevantToAccountType, mostRelevantToClientId); + } + + if (mostRelevantToOfficeId != null) { + toOffice = this.officeReadPlatformService.retrieveOffice(mostRelevantToOfficeId); + toOfficeOptions = this.officeReadPlatformService.retrieveAllOfficesForDropdown(); + + toClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantToOfficeId); + if (toClientOptions != null && toClientOptions.size() == 1) { + toClient = new ArrayList(toClientOptions).get(0); + + toAccountOptions = retrieveToAccounts(fromAccount, mostRelevantToAccountType, mostRelevantToClientId); + } + } + + final Collection transferTypeOptions = Arrays.asList(transferType(AccountTransferType.ACCOUNT_TRANSFER), + transferType(AccountTransferType.LOAN_REPAYMENT)/* + * , + * transferType( + * AccountTransferType + * . + * CHARGE_PAYMENT + * ) + */); + final Collection statusOptions = Arrays.asList(standingInstructionStatus(StandingInstructionStatus.ACTIVE), + standingInstructionStatus(StandingInstructionStatus.DISABLED)); + final Collection instructionTypeOptions = Arrays.asList(standingInstructionType(StandingInstructionType.FIXED), + standingInstructionType(StandingInstructionType.DUES)); + final Collection priorityOptions = Arrays.asList(standingInstructionPriority(StandingInstructionPriority.URGENT), + standingInstructionPriority(StandingInstructionPriority.HIGH), + standingInstructionPriority(StandingInstructionPriority.MEDIUM), + standingInstructionPriority(StandingInstructionPriority.LOW)); + final Collection recurrenceTypeOptions = Arrays.asList(recurrenceType(AccountTransferRecurrenceType.PERIODIC), + recurrenceType(AccountTransferRecurrenceType.AS_PER_DUES)); + final Collection recurrenceFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions(); + + return StandingInstructionData.template(fromOffice, fromClient, fromAccountTypeData, fromAccount, transferDate, toOffice, toClient, + toAccountTypeData, toAccount, fromOfficeOptions, fromClientOptions, fromAccountTypeOptions, fromAccountOptions, + toOfficeOptions, toClientOptions, toAccountTypeOptions, toAccountOptions, transferTypeOptions, statusOptions, + instructionTypeOptions, priorityOptions, recurrenceTypeOptions, recurrenceFrequencyOptions); + } + + private Collection retrieveToAccounts(final PortfolioAccountData excludeThisAccountFromOptions, + final Integer toAccountType, final Long toClientId) { + + final String currencyCode = excludeThisAccountFromOptions != null ? excludeThisAccountFromOptions.currencyCode() : null; + + Collection accountOptions = this.portfolioAccountReadPlatformService.retrieveAllForLookup(toAccountType, + toClientId, currencyCode, null); + if (!CollectionUtils.isEmpty(accountOptions)) { + accountOptions.remove(excludeThisAccountFromOptions); + } else { + accountOptions = null; + } + + return accountOptions; + } + + @Override + public Page retrieveAll(final StandingInstructionDTO standingInstructionDTO) { + + final StringBuilder sqlBuilder = new StringBuilder(200); + sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append(this.standingInstructionMapper.schema()); + if (standingInstructionDTO.transferType() != null || standingInstructionDTO.clientId() != null + || standingInstructionDTO.clientName() != null) { + sqlBuilder.append(" where "); + } + boolean addAndCaluse = false; + List paramObj = new ArrayList(); + if (standingInstructionDTO.transferType() != null) { + if (addAndCaluse) { + sqlBuilder.append(" and "); + } + sqlBuilder.append(" atd.transfer_type=? "); + paramObj.add(standingInstructionDTO.transferType()); + addAndCaluse = true; + } + if (standingInstructionDTO.clientId() != null) { + if (addAndCaluse) { + sqlBuilder.append(" and "); + } + sqlBuilder.append(" fromclient.id=? "); + paramObj.add(standingInstructionDTO.clientId()); + addAndCaluse = true; + } else if (standingInstructionDTO.clientName() != null) { + if (addAndCaluse) { + sqlBuilder.append(" and "); + } + sqlBuilder.append(" fromclient.display_name=? "); + paramObj.add(standingInstructionDTO.clientName()); + addAndCaluse = true; + } + + if (standingInstructionDTO.fromAccountType() != null && standingInstructionDTO.fromAccount() != null) { + PortfolioAccountType accountType = PortfolioAccountType.fromInt(standingInstructionDTO.fromAccountType()); + if (addAndCaluse) { + sqlBuilder.append(" and "); + } + if (accountType.isSavingsAccount()) { + sqlBuilder.append(" fromsavacc.id=? "); + paramObj.add(standingInstructionDTO.fromAccount()); + } else if (accountType.isLoanAccount()) { + sqlBuilder.append(" fromloanacc.id=? "); + paramObj.add(standingInstructionDTO.fromAccount()); + } + addAndCaluse = true; + } + + final SearchParameters searchParameters = standingInstructionDTO.searchParameters(); + if (searchParameters.isOrderByRequested()) { + sqlBuilder.append(" order by ").append(searchParameters.getOrderBy()); + + if (searchParameters.isSortOrderProvided()) { + sqlBuilder.append(' ').append(searchParameters.getSortOrder()); + } + } + + if (searchParameters.isLimited()) { + sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + if (searchParameters.isOffset()) { + sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + } + } + + final Object[] finalObjectArray = paramObj.toArray(); + final String sqlCountRows = "SELECT FOUND_ROWS()"; + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), finalObjectArray, + this.standingInstructionMapper); + } + + @Override + public Collection retrieveAll(final Integer status) { + final StringBuilder sqlBuilder = new StringBuilder(200); + sqlBuilder.append("select "); + sqlBuilder.append(this.standingInstructionMapper.schema()); + sqlBuilder + .append(" where atsi.status=? and CURRENT_DATE() >= atsi.valid_from and (atsi.valid_till IS NULL or CURRENT_DATE() < atsi.valid_till) ") + .append(" and (atsi.last_run_date <> CURRENT_DATE() or atsi.last_run_date IS NULL)") + .append(" ORDER BY atsi.priority DESC"); + return this.jdbcTemplate.query(sqlBuilder.toString(), this.standingInstructionMapper, status); + } + + @Override + public StandingInstructionData retrieveOne(final Long instructionId) { + + try { + final String sql = "select " + this.standingInstructionMapper.schema() + " where atsi.id = ?"; + + return this.jdbcTemplate.queryForObject(sql, this.standingInstructionMapper, new Object[] { instructionId }); + } catch (final EmptyResultDataAccessException e) { + throw new AccountTransferNotFoundException(instructionId); + } + } + + @Override + public StandingInstructionDuesData retriveLoanDuesData(final Long loanId) { + final StandingInstructionLoanDuesMapper rm = new StandingInstructionLoanDuesMapper(); + final String sql = "select " + rm.schema() + " where ml.id= ? and ls.duedate <= CURRENT_DATE() and ls.completed_derived <> 1"; + return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { loanId }); + } + + private static final class StandingInstructionMapper implements RowMapper { + + private final String schemaSql; + + public StandingInstructionMapper() { + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("atsi.id as id,atsi.name as name, atsi.priority as priority,"); + sqlBuilder.append("atsi.status as status, atsi.instruction_type as instructionType,"); + sqlBuilder.append("atsi.amount as amount,"); + sqlBuilder.append("atsi.valid_from as validFrom, atsi.valid_till as validTill,"); + sqlBuilder.append("atsi.recurrence_type as recurrenceType, atsi.recurrence_frequency as recurrenceFrequency,"); + sqlBuilder.append("atsi.recurrence_interval as recurrenceInterval, atsi.recurrence_on_day as recurrenceOnDay,"); + sqlBuilder.append("atsi.recurrence_on_month as recurrenceOnMonth,"); + sqlBuilder.append("atd.id as accountDetailId,atd.transfer_type as transferType,"); + sqlBuilder.append("fromoff.id as fromOfficeId, fromoff.name as fromOfficeName,"); + sqlBuilder.append("tooff.id as toOfficeId, tooff.name as toOfficeName,"); + sqlBuilder.append("fromclient.id as fromClientId, fromclient.display_name as fromClientName,"); + sqlBuilder.append("toclient.id as toClientId, toclient.display_name as toClientName,"); + sqlBuilder.append("fromsavacc.id as fromSavingsAccountId, fromsavacc.account_no as fromSavingsAccountNo,"); + sqlBuilder.append("fromsp.id as fromProductId, fromsp.name as fromProductName, "); + sqlBuilder.append("fromloanacc.id as fromLoanAccountId, fromloanacc.account_no as fromLoanAccountNo,"); + sqlBuilder.append("fromlp.id as fromLoanProductId, fromlp.name as fromLoanProductName,"); + sqlBuilder.append("tosavacc.id as toSavingsAccountId, tosavacc.account_no as toSavingsAccountNo,"); + sqlBuilder.append("tosp.id as toProductId, tosp.name as toProductName, "); + sqlBuilder.append("toloanacc.id as toLoanAccountId, toloanacc.account_no as toLoanAccountNo, "); + sqlBuilder.append("tolp.id as toLoanProductId, tolp.name as toLoanProductName "); + sqlBuilder.append(" FROM m_account_transfer_standing_instructions atsi "); + sqlBuilder.append("join m_account_transfer_details atd on atd.id = atsi.account_transfer_details_id "); + sqlBuilder.append("join m_office fromoff on fromoff.id = atd.from_office_id "); + sqlBuilder.append("join m_office tooff on tooff.id = atd.to_office_id "); + sqlBuilder.append("join m_client fromclient on fromclient.id = atd.from_client_id "); + sqlBuilder.append("join m_client toclient on toclient.id = atd.to_client_id "); + sqlBuilder.append("left join m_savings_account fromsavacc on fromsavacc.id = atd.from_savings_account_id "); + sqlBuilder.append("left join m_savings_product fromsp ON fromsavacc.product_id = fromsp.id "); + sqlBuilder.append("left join m_loan fromloanacc on fromloanacc.id = atd.from_loan_account_id "); + sqlBuilder.append("left join m_product_loan fromlp ON fromloanacc.product_id = fromlp.id "); + sqlBuilder.append("left join m_savings_account tosavacc on tosavacc.id = atd.to_savings_account_id "); + sqlBuilder.append("left join m_savings_product tosp ON tosavacc.product_id = tosp.id "); + sqlBuilder.append("left join m_loan toloanacc on toloanacc.id = atd.to_loan_account_id "); + sqlBuilder.append("left join m_product_loan tolp ON toloanacc.product_id = tolp.id "); + + this.schemaSql = sqlBuilder.toString(); + } + + public String schema() { + return this.schemaSql; + } + + @Override + public StandingInstructionData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = rs.getLong("id"); + final Long accountDetailId = rs.getLong("accountDetailId"); + final String name = rs.getString("name"); + final Integer priority = JdbcSupport.getInteger(rs, "priority"); + EnumOptionData priorityEnum = AccountTransferEnumerations.standingInstructionPriority(priority); + + final Integer status = JdbcSupport.getInteger(rs, "status"); + EnumOptionData statusEnum = AccountTransferEnumerations.standingInstructionStatus(status); + final Integer instructionType = JdbcSupport.getInteger(rs, "instructionType"); + EnumOptionData instructionTypeEnum = AccountTransferEnumerations.standingInstructionType(instructionType); + final LocalDate validFrom = JdbcSupport.getLocalDate(rs, "validFrom"); + final LocalDate validTill = JdbcSupport.getLocalDate(rs, "validTill"); + final BigDecimal transferAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "amount"); + final Integer recurrenceType = JdbcSupport.getInteger(rs, "recurrenceType"); + EnumOptionData recurrenceTypeEnum = AccountTransferEnumerations.recurrenceType(recurrenceType); + final Integer recurrenceFrequency = JdbcSupport.getInteger(rs, "recurrenceFrequency"); + + EnumOptionData recurrenceFrequencyEnum = null; + if (recurrenceFrequency != null) { + recurrenceFrequencyEnum = CommonEnumerations.termFrequencyType(recurrenceFrequency, "recurrence"); + } + final Integer recurrenceInterval = JdbcSupport.getInteger(rs, "recurrenceInterval"); + + MonthDay recurrenceOnMonthDay = null; + final Integer recurrenceOnDay = JdbcSupport.getInteger(rs, "recurrenceOnDay"); + final Integer recurrenceOnMonth = JdbcSupport.getInteger(rs, "recurrenceOnMonth"); + if (recurrenceOnDay != null) { + recurrenceOnMonthDay = new MonthDay(recurrenceOnMonth, recurrenceOnDay); + } + + final Integer transferType = rs.getInt("transferType"); + EnumOptionData transferTypeEnum = AccountTransferEnumerations.transferType(transferType); + + /* + * 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 Long fromOfficeId = JdbcSupport.getLong(rs, "fromOfficeId"); + final String fromOfficeName = rs.getString("fromOfficeName"); + final OfficeData fromOffice = OfficeData.dropdown(fromOfficeId, fromOfficeName, null); + + final Long toOfficeId = JdbcSupport.getLong(rs, "toOfficeId"); + final String toOfficeName = rs.getString("toOfficeName"); + final OfficeData toOffice = OfficeData.dropdown(toOfficeId, toOfficeName, null); + + final Long fromClientId = JdbcSupport.getLong(rs, "fromClientId"); + final String fromClientName = rs.getString("fromClientName"); + final ClientData fromClient = ClientData.lookup(fromClientId, fromClientName, fromOfficeId, fromOfficeName); + + final Long toClientId = JdbcSupport.getLong(rs, "toClientId"); + final String toClientName = rs.getString("toClientName"); + final ClientData toClient = ClientData.lookup(toClientId, toClientName, toOfficeId, toOfficeName); + + final Long fromSavingsAccountId = JdbcSupport.getLong(rs, "fromSavingsAccountId"); + final String fromSavingsAccountNo = rs.getString("fromSavingsAccountNo"); + final Long fromProductId = JdbcSupport.getLong(rs, "fromProductId"); + final String fromProductName = rs.getString("fromProductName"); + final Long fromLoanAccountId = JdbcSupport.getLong(rs, "fromLoanAccountId"); + final String fromLoanAccountNo = rs.getString("fromLoanAccountNo"); + final Long fromLoanProductId = JdbcSupport.getLong(rs, "fromLoanProductId"); + final String fromLoanProductName = rs.getString("fromLoanProductName"); + PortfolioAccountData fromAccount = null; + EnumOptionData fromAccountType = null; + if (fromSavingsAccountId != null) { + fromAccount = new PortfolioAccountData(fromSavingsAccountId, fromSavingsAccountNo, null, null, null, null, null, + fromProductId, fromProductName, null, null, null); + fromAccountType = accountType(PortfolioAccountType.SAVINGS); + } else if (fromLoanAccountId != null) { + fromAccount = new PortfolioAccountData(fromLoanAccountId, fromLoanAccountNo, null, null, null, null, null, + fromLoanProductId, fromLoanProductName, null, null, null); + fromAccountType = accountType(PortfolioAccountType.LOAN); + } + + PortfolioAccountData toAccount = null; + EnumOptionData toAccountType = null; + final Long toSavingsAccountId = JdbcSupport.getLong(rs, "toSavingsAccountId"); + final String toSavingsAccountNo = rs.getString("toSavingsAccountNo"); + final Long toProductId = JdbcSupport.getLong(rs, "toProductId"); + final String toProductName = rs.getString("toProductName"); + final Long toLoanAccountId = JdbcSupport.getLong(rs, "toLoanAccountId"); + final String toLoanAccountNo = rs.getString("toLoanAccountNo"); + final Long toLoanProductId = JdbcSupport.getLong(rs, "toLoanProductId"); + final String toLoanProductName = rs.getString("toLoanProductName"); + + if (toSavingsAccountId != null) { + toAccount = new PortfolioAccountData(toSavingsAccountId, toSavingsAccountNo, null, null, null, null, null, toProductId, + toProductName, null, null, null); + toAccountType = accountType(PortfolioAccountType.SAVINGS); + } else if (toLoanAccountId != null) { + toAccount = new PortfolioAccountData(toLoanAccountId, toLoanAccountNo, null, null, null, null, null, toLoanProductId, + toLoanProductName, null, null, null); + toAccountType = accountType(PortfolioAccountType.LOAN); + } + + return StandingInstructionData.instance(id, accountDetailId, name, fromOffice, toOffice, fromClient, toClient, fromAccountType, + fromAccount, toAccountType, toAccount, transferTypeEnum, priorityEnum, instructionTypeEnum, statusEnum, transferAmount, + validFrom, validTill, recurrenceTypeEnum, recurrenceFrequencyEnum, recurrenceInterval, recurrenceOnMonthDay); + } + } + + private static final class StandingInstructionLoanDuesMapper implements RowMapper { + + private final String schemaSql; + + public StandingInstructionLoanDuesMapper() { + final StringBuilder sqlBuilder = new StringBuilder(400); + + sqlBuilder.append("max(ls.duedate) as dueDate,sum(ls.principal_amount) as principalAmount,"); + sqlBuilder.append("sum(ls.principal_completed_derived) as principalCompleted,"); + sqlBuilder.append("sum(ls.principal_writtenoff_derived) as principalWrittenOff,"); + sqlBuilder.append("sum(ls.interest_amount) as interestAmount,"); + sqlBuilder.append("sum(ls.interest_completed_derived) as interestCompleted,"); + sqlBuilder.append("sum(ls.interest_writtenoff_derived) as interestWrittenOff,"); + sqlBuilder.append("sum(ls.interest_waived_derived) as interestWaived,"); + sqlBuilder.append("sum(ls.penalty_charges_amount) as penalityAmount,"); + sqlBuilder.append("sum(ls.penalty_charges_completed_derived) as penalityCompleted,"); + sqlBuilder.append("sum(ls.penalty_charges_writtenoff_derived)as penaltyWrittenOff,"); + sqlBuilder.append("sum(ls.penalty_charges_waived_derived) as penaltyWaived,"); + sqlBuilder.append("sum(ls.fee_charges_amount) as feeAmount,"); + sqlBuilder.append("sum(ls.fee_charges_completed_derived) as feecompleted,"); + sqlBuilder.append("sum(ls.fee_charges_writtenoff_derived) as feeWrittenOff,"); + sqlBuilder.append("sum(ls.fee_charges_waived_derived) as feeWaived "); + sqlBuilder.append("from m_loan_repayment_schedule ls "); + sqlBuilder.append(" join m_loan ml on ml.id = ls.loan_id "); + + this.schemaSql = sqlBuilder.toString(); + } + + public String schema() { + return this.schemaSql; + } + + @Override + public StandingInstructionDuesData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate"); + final BigDecimal principalDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalAmount"); + final BigDecimal principalPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalCompleted"); + final BigDecimal principalWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalWrittenOff"); + final BigDecimal principalOutstanding = principalDue.subtract(principalPaid).subtract(principalWrittenOff); + + final BigDecimal interestExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestAmount"); + final BigDecimal interestPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestCompleted"); + final BigDecimal interestWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWrittenOff"); + final BigDecimal interestWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWaived"); + final BigDecimal interestActualDue = interestExpectedDue.subtract(interestWaived).subtract(interestWrittenOff); + final BigDecimal interestOutstanding = interestActualDue.subtract(interestPaid); + + final BigDecimal penaltyChargesExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penalityAmount"); + final BigDecimal penaltyChargesPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penalityCompleted"); + final BigDecimal penaltyChargesWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyWrittenOff"); + final BigDecimal penaltyChargesWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyWaived"); + final BigDecimal penaltyChargesActualDue = penaltyChargesExpectedDue.subtract(penaltyChargesWaived).subtract( + penaltyChargesWrittenOff); + final BigDecimal penaltyChargesOutstanding = penaltyChargesActualDue.subtract(penaltyChargesPaid); + + final BigDecimal feeChargesExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeAmount"); + final BigDecimal feeChargesPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feecompleted"); + final BigDecimal feeChargesWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeWrittenOff"); + final BigDecimal feeChargesWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeWaived"); + final BigDecimal feeChargesActualDue = feeChargesExpectedDue.subtract(feeChargesWaived).subtract(feeChargesWrittenOff); + final BigDecimal feeChargesOutstanding = feeChargesActualDue.subtract(feeChargesPaid); + + final BigDecimal totalOutstanding = principalOutstanding.add(interestOutstanding).add(feeChargesOutstanding) + .add(penaltyChargesOutstanding); + + return new StandingInstructionDuesData(dueDate, totalOutstanding); + } + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/StandingInstructionWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/StandingInstructionWritePlatformService.java new file mode 100755 index 000000000..83c668ae6 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/StandingInstructionWritePlatformService.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.account.service; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.jobs.exception.JobExecutionException; + +public interface StandingInstructionWritePlatformService { + + CommandProcessingResult create(JsonCommand command); + + CommandProcessingResult update(Long id, JsonCommand command); + + void executeStandingInstructions() throws JobExecutionException; + + CommandProcessingResult delete(Long id); + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/StandingInstructionWritePlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/StandingInstructionWritePlatformServiceImpl.java new file mode 100755 index 000000000..39ce11143 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/StandingInstructionWritePlatformServiceImpl.java @@ -0,0 +1,245 @@ +/** + * 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.account.service; + +import static org.mifosplatform.portfolio.account.AccountDetailConstants.fromAccountTypeParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.fromClientIdParamName; +import static org.mifosplatform.portfolio.account.AccountDetailConstants.toAccountTypeParamName; +import static org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants.statusParamName; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.joda.time.LocalDate; +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.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; +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.account.PortfolioAccountType; +import org.mifosplatform.portfolio.account.api.StandingInstructionApiConstants; +import org.mifosplatform.portfolio.account.data.AccountTransferDTO; +import org.mifosplatform.portfolio.account.data.StandingInstructionData; +import org.mifosplatform.portfolio.account.data.StandingInstructionDataValidator; +import org.mifosplatform.portfolio.account.data.StandingInstructionDuesData; +import org.mifosplatform.portfolio.account.domain.AccountTransferDetailRepository; +import org.mifosplatform.portfolio.account.domain.AccountTransferDetails; +import org.mifosplatform.portfolio.account.domain.AccountTransferRecurrenceType; +import org.mifosplatform.portfolio.account.domain.AccountTransferStandingInstruction; +import org.mifosplatform.portfolio.account.domain.StandingInstructionAssembler; +import org.mifosplatform.portfolio.account.domain.StandingInstructionRepository; +import org.mifosplatform.portfolio.account.domain.StandingInstructionStatus; +import org.mifosplatform.portfolio.account.domain.StandingInstructionType; +import org.mifosplatform.portfolio.account.exception.StandingInstructionNotFoundException; +import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType; +import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator; +import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.ScheduledDateGenerator; +import org.mifosplatform.portfolio.savings.exception.InsufficientAccountBalanceException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class StandingInstructionWritePlatformServiceImpl implements StandingInstructionWritePlatformService { + + private final static Logger logger = LoggerFactory.getLogger(StandingInstructionWritePlatformServiceImpl.class); + + private final StandingInstructionDataValidator standingInstructionDataValidator; + private final StandingInstructionAssembler standingInstructionAssembler; + private final AccountTransferDetailRepository accountTransferDetailRepository; + private final StandingInstructionRepository standingInstructionRepository; + private final StandingInstructionReadPlatformService standingInstructionReadPlatformService; + private final AccountTransfersWritePlatformService accountTransfersWritePlatformService; + + @Autowired + public StandingInstructionWritePlatformServiceImpl(final StandingInstructionDataValidator standingInstructionDataValidator, + final StandingInstructionAssembler standingInstructionAssembler, + final AccountTransferDetailRepository accountTransferDetailRepository, + final StandingInstructionRepository standingInstructionRepository, + final StandingInstructionReadPlatformService standingInstructionReadPlatformService, + final AccountTransfersWritePlatformService accountTransfersWritePlatformService) { + this.standingInstructionDataValidator = standingInstructionDataValidator; + this.standingInstructionAssembler = standingInstructionAssembler; + this.accountTransferDetailRepository = accountTransferDetailRepository; + this.standingInstructionRepository = standingInstructionRepository; + this.standingInstructionReadPlatformService = standingInstructionReadPlatformService; + this.accountTransfersWritePlatformService = accountTransfersWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult create(final JsonCommand command) { + + this.standingInstructionDataValidator.validateForCreate(command); + + final Integer fromAccountTypeId = command.integerValueSansLocaleOfParameterNamed(fromAccountTypeParamName); + final PortfolioAccountType fromAccountType = PortfolioAccountType.fromInt(fromAccountTypeId); + + final Integer toAccountTypeId = command.integerValueSansLocaleOfParameterNamed(toAccountTypeParamName); + final PortfolioAccountType toAccountType = PortfolioAccountType.fromInt(toAccountTypeId); + + final Long fromClientId = command.longValueOfParameterNamed(fromClientIdParamName); + + Long standingInstructionId = null; + try { + if (isSavingsToSavingsAccountTransfer(fromAccountType, toAccountType)) { + final AccountTransferDetails standingInstruction = this.standingInstructionAssembler + .assembleSavingsToSavingsTransfer(command); + this.accountTransferDetailRepository.save(standingInstruction); + standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId(); + } else if (isSavingsToLoanAccountTransfer(fromAccountType, toAccountType)) { + final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleSavingsToLoanTransfer(command); + this.accountTransferDetailRepository.save(standingInstruction); + standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId(); + } else if (isLoanToSavingsAccountTransfer(fromAccountType, toAccountType)) { + + final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleLoanToSavingsTransfer(command); + this.accountTransferDetailRepository.save(standingInstruction); + standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId(); + + } + } catch (final DataIntegrityViolationException dve) { + handleDataIntegrityIssues(command, dve); + return CommandProcessingResult.empty(); + } + final CommandProcessingResultBuilder builder = new CommandProcessingResultBuilder().withEntityId(standingInstructionId) + .withClientId(fromClientId); + return builder.build(); + } + + private void handleDataIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) { + + final Throwable realCause = dve.getMostSpecificCause(); + if (realCause.getMessage().contains("name")) { + final String name = command.stringValueOfParameterNamed(StandingInstructionApiConstants.nameParamName); + throw new PlatformDataIntegrityException("error.msg.standinginstruction.duplicate.name", "Standinginstruction with name `" + + name + "` already exists", "name", name); + } + logger.error(dve.getMessage(), dve); + throw new PlatformDataIntegrityException("error.msg.client.unknown.data.integrity.issue", + "Unknown data integrity issue with resource."); + } + + private boolean isLoanToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) { + return fromAccountType.isLoanAccount() && toAccountType.isSavingsAccount(); + } + + private boolean isSavingsToLoanAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) { + return fromAccountType.isSavingsAccount() && toAccountType.isLoanAccount(); + } + + private boolean isSavingsToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) { + return fromAccountType.isSavingsAccount() && toAccountType.isSavingsAccount(); + } + + @Override + public CommandProcessingResult update(final Long id, final JsonCommand command) { + this.standingInstructionDataValidator.validateForUpdate(command); + AccountTransferStandingInstruction standingInstructionsForUpdate = this.standingInstructionRepository.findOne(id); + if (standingInstructionsForUpdate == null) { throw new StandingInstructionNotFoundException(id); } + final Map actualChanges = standingInstructionsForUpdate.update(command); + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withEntityId(id) // + .with(actualChanges) // + .build(); + } + + @Override + public CommandProcessingResult delete(final Long id) { + AccountTransferStandingInstruction standingInstructionsForUpdate = this.standingInstructionRepository.findOne(id); + standingInstructionsForUpdate.updateStatus(StandingInstructionStatus.DELETED.getValue()); + final Map actualChanges = new HashMap(); + actualChanges.put(statusParamName, StandingInstructionStatus.DELETED.getValue()); + return new CommandProcessingResultBuilder() // + .withEntityId(id) // + .with(actualChanges) // + .build(); + } + + @Override + @CronTarget(jobName = JobName.EXECUTE_STANDING_INSTRUCTIONS) + public void executeStandingInstructions() throws JobExecutionException { + Collection instructionDatas = this.standingInstructionReadPlatformService + .retrieveAll(StandingInstructionStatus.ACTIVE.getValue()); + final StringBuilder sb = new StringBuilder(); + for (StandingInstructionData data : instructionDatas) { + boolean isDueForTransfer = false; + AccountTransferRecurrenceType recurrenceType = data.recurrenceType(); + StandingInstructionType instructionType = data.instructionType(); + LocalDate transactionDate = new LocalDate(); + if (recurrenceType.isPeriodicRecurrence()) { + final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator(); + PeriodFrequencyType frequencyType = data.recurrenceFrequency(); + LocalDate startDate = data.validFrom(); + if (frequencyType.isMonthly()) { + startDate = startDate.withDayOfMonth(data.recurrenceOnDay()); + if (startDate.isBefore(data.validFrom())) { + startDate = startDate.plusMonths(1); + } + } else if (frequencyType.isYearly()) { + startDate = startDate.withDayOfMonth(data.recurrenceOnDay()).withMonthOfYear(data.recurrenceOnMonth()); + if (startDate.isBefore(data.validFrom())) { + startDate = startDate.plusYears(1); + } + } + isDueForTransfer = scheduledDateGenerator.isDateFallsInSchedule(frequencyType, data.recurrenceInterval(), startDate, + transactionDate); + + } + BigDecimal transactionAmount = data.amount(); + if (data.toAccountType().isLoanAccount() + && (recurrenceType.isDuesRecurrence() || (isDueForTransfer && instructionType.isDuesAmoutTransfer()))) { + StandingInstructionDuesData standingInstructionDuesData = this.standingInstructionReadPlatformService + .retriveLoanDuesData(data.toAccount().accountId()); + if (data.instructionType().isDuesAmoutTransfer()) { + transactionAmount = standingInstructionDuesData.totalDueAmount(); + } + if (recurrenceType.isDuesRecurrence()) { + isDueForTransfer = new LocalDate().equals(standingInstructionDuesData.dueDate()); + } + } + + if (isDueForTransfer && transactionAmount != null && transactionAmount.compareTo(BigDecimal.ZERO) > 0) { + final AccountTransferDetails accountTransferDetails = this.accountTransferDetailRepository.findOne(data.accountDetailId()); + accountTransferDetails.accountTransferStandingInstruction().updateLatsRunDate(transactionDate.toDate()); + AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, transactionAmount, data.fromAccountType(), + data.toAccountType(), data.fromAccount().accountId(), data.toAccount().accountId(), data.name() + + " Standing instruction trasfer ", null, null, null, null, data.toTransferType(), null, null, data + .transferType().getValue(), accountTransferDetails); + transferAmount(sb, accountTransferDTO, data.getId()); + } + } + if (sb.length() > 0) { throw new JobExecutionException(sb.toString()); } + + } + + /** + * @param sb + * @param accountTransferDTO + */ + private void transferAmount(final StringBuilder sb, final AccountTransferDTO accountTransferDTO, final Long instructionId) { + try { + this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); + } catch (final PlatformApiDataValidationException e) { + sb.append("Validation exception while trasfering funds for standing Instruction id").append(instructionId).append(" from ") + .append(accountTransferDTO.getFromAccountId()).append(" to ").append(accountTransferDTO.getToAccountId()) + .append("--------"); + } catch (final InsufficientAccountBalanceException e) { + sb.append("InsufficientAccountBalance Exception while trasfering funds for standing Instruction id").append(instructionId) + .append(" from ").append(accountTransferDTO.getFromAccountId()).append(" to ") + .append(accountTransferDTO.getToAccountId()).append("--------"); + } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/domain/PeriodFrequencyType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/domain/PeriodFrequencyType.java index 8e8434b33..b6aba7dca 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/domain/PeriodFrequencyType.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/domain/PeriodFrequencyType.java @@ -5,6 +5,7 @@ */ package org.mifosplatform.portfolio.common.domain; + public enum PeriodFrequencyType { DAYS(0, "periodFrequencyType.days"), // WEEKS(1, "periodFrequencyType.weeks"), // @@ -48,4 +49,12 @@ public enum PeriodFrequencyType { } return repaymentFrequencyType; } + + public boolean isMonthly() { + return this.value.equals(PeriodFrequencyType.MONTHS.getValue()); + } + + public boolean isYearly() { + return this.value.equals(PeriodFrequencyType.MONTHS.getValue()); + } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/service/CommonEnumerations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/service/CommonEnumerations.java index 33c06eb78..b10966fb1 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/service/CommonEnumerations.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/service/CommonEnumerations.java @@ -24,6 +24,10 @@ public class CommonEnumerations { optionData = new EnumOptionData(PeriodFrequencyType.MONTHS.getValue().longValue(), codePrefix + PeriodFrequencyType.MONTHS.getCode(), "Months"); break; + case YEARS: + optionData = new EnumOptionData(PeriodFrequencyType.YEARS.getValue().longValue(), codePrefix + + PeriodFrequencyType.YEARS.getCode(), "Years"); + break; default: optionData = new EnumOptionData(PeriodFrequencyType.INVALID.getValue().longValue(), PeriodFrequencyType.INVALID.getCode(), "Invalid"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/service/DropdownReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/service/DropdownReadPlatformServiceImpl.java index 6fb30cba8..43eca29c1 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/service/DropdownReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/service/DropdownReadPlatformServiceImpl.java @@ -1,6 +1,6 @@ package org.mifosplatform.portfolio.common.service; -import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.loanTermFrequencyType; +import static org.mifosplatform.portfolio.common.service.CommonEnumerations.termFrequencyType; import java.util.Arrays; import java.util.List; @@ -10,13 +10,13 @@ import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType; import org.springframework.stereotype.Service; @Service -public class DropdownReadPlatformServiceImpl implements DropdownReadPlatformService{ +public class DropdownReadPlatformServiceImpl implements DropdownReadPlatformService { @Override public List retrievePeriodFrequencyTypeOptions() { - final List loanTermFrequencyOptions = Arrays.asList(loanTermFrequencyType(PeriodFrequencyType.DAYS), - loanTermFrequencyType(PeriodFrequencyType.WEEKS), loanTermFrequencyType(PeriodFrequencyType.MONTHS), - loanTermFrequencyType(PeriodFrequencyType.YEARS)); + final List loanTermFrequencyOptions = Arrays.asList(termFrequencyType(PeriodFrequencyType.DAYS, "frequency"), + termFrequencyType(PeriodFrequencyType.WEEKS, "frequency"), termFrequencyType(PeriodFrequencyType.MONTHS, "frequency"), + termFrequencyType(PeriodFrequencyType.YEARS, "frequency")); return loanTermFrequencyOptions; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java index 27bacd135..a3465a1f5 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java @@ -28,8 +28,8 @@ import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.organisation.workingdays.domain.WorkingDays; import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; -import org.mifosplatform.portfolio.account.domain.AccountTransfer; import org.mifosplatform.portfolio.account.domain.AccountTransferRepository; +import org.mifosplatform.portfolio.account.domain.AccountTransferTransaction; import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.client.exception.ClientNotActiveException; import org.mifosplatform.portfolio.group.domain.Group; @@ -277,7 +277,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { } private void updateLoanTransaction(final Long loanTransactionId, final LoanTransaction newLoanTransaction) { - final AccountTransfer transferTransaction = this.accountTransferRepository.findByToLoanTransactionId(loanTransactionId); + final AccountTransferTransaction transferTransaction = this.accountTransferRepository.findByToLoanTransactionId(loanTransactionId); if (transferTransaction != null) { transferTransaction.updateToLoanTransaction(newLoanTransaction); this.accountTransferRepository.save(transferTransaction); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java index 56911ced1..1f9016a86 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java @@ -7,7 +7,11 @@ package org.mifosplatform.portfolio.loanaccount.loanschedule.domain; import java.util.List; +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.organisation.holiday.domain.Holiday; import org.mifosplatform.organisation.holiday.service.HolidayUtil; import org.mifosplatform.organisation.workingdays.domain.WorkingDays; @@ -81,6 +85,45 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator { return dueRepaymentPeriodDate; } + @Override + public Boolean isDateFallsInSchedule(final PeriodFrequencyType frequency, final int repaidEvery, final LocalDate startDate, + final LocalDate date) { + boolean isScheduledDate = false; + switch (frequency) { + case DAYS: + int diff = Days.daysBetween(startDate, date).getDays(); + isScheduledDate = (diff % repaidEvery) == 0; + break; + case WEEKS: + int weekDiff =Weeks.weeksBetween(startDate, date).getWeeks(); + isScheduledDate = (weekDiff % repaidEvery) == 0; + if(isScheduledDate){ + LocalDate modifiedDate = startDate.plusWeeks(weekDiff); + isScheduledDate = modifiedDate.isEqual(date); + } + break; + case MONTHS: + int monthDiff =Months.monthsBetween(startDate, date).getMonths(); + isScheduledDate = (monthDiff % repaidEvery) == 0; + if(isScheduledDate){ + LocalDate modifiedDate = startDate.plusMonths(monthDiff); + isScheduledDate = modifiedDate.isEqual(date); + } + break; + case YEARS: + int yearDiff =Years.yearsBetween(startDate, date).getYears(); + isScheduledDate = (yearDiff % repaidEvery) == 0; + if(isScheduledDate){ + LocalDate modifiedDate = startDate.plusYears(yearDiff); + isScheduledDate = modifiedDate.isEqual(date); + } + break; + case INVALID: + break; + } + return isScheduledDate; + } + @Override public LocalDate idealDisbursementDateBasedOnFirstRepaymentDate(final PeriodFrequencyType repaymentPeriodFrequencyType, final int repaidEvery, final LocalDate firstRepaymentDate) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/ScheduledDateGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/ScheduledDateGenerator.java index 0406c8e0f..16bb22093 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/ScheduledDateGenerator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/ScheduledDateGenerator.java @@ -27,4 +27,6 @@ public interface ScheduledDateGenerator { LocalDate getRepaymentPeriodDate(PeriodFrequencyType frequency, int repaidEvery, LocalDate startDate); + Boolean isDateFallsInSchedule(PeriodFrequencyType frequency, int repaidEvery, LocalDate startDate, LocalDate date); + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java index f392220c1..2a29bfd85 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java @@ -135,6 +135,11 @@ public class LoanAssembler { return loanAccount; } + public void setHelpers(final Loan loanAccount) { + loanAccount.setHelpers(defaultLoanLifecycleStateMachine(), this.loanSummaryWrapper, + this.loanRepaymentScheduleTransactionProcessorFactory); + } + public Loan assembleFrom(final JsonCommand command, final AppUser currentUser) { final JsonElement element = command.parsedJson(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index 39ecb9462..4eb85c95e 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -983,8 +983,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { + " left JOIN m_payment_detail pd ON tr.payment_detail_id = pd.id" + " left join m_code_value cv on pd.payment_type_cv_id = cv.id" + " left join m_office office on office.id=tr.office_id" - + " left join m_savings_account_transfer fromtran on fromtran.from_loan_transaction_id = tr.id " - + " left join m_savings_account_transfer totran on totran.to_loan_transaction_id = tr.id "; + + " left join m_account_transfer_transaction fromtran on fromtran.from_loan_transaction_id = tr.id " + + " left join m_account_transfer_transaction totran on totran.to_loan_transaction_id = tr.id "; } @Override @@ -1071,8 +1071,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { + " totran.description as toTransferDescription " + " from m_loan l join m_loan_transaction tr on tr.loan_id = l.id" + " join m_currency rc on rc.`code` = l.currency_code " - + " left join m_savings_account_transfer fromtran on fromtran.from_loan_transaction_id = tr.id " - + " left join m_savings_account_transfer totran on totran.to_loan_transaction_id = tr.id "; + + " left join m_account_transfer_transaction fromtran on fromtran.from_loan_transaction_id = tr.id " + + " left join m_account_transfer_transaction totran on totran.to_loan_transaction_id = tr.id "; } @Override 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 2966dfb31..668884b45 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 @@ -47,8 +47,9 @@ import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWr import org.mifosplatform.portfolio.account.PortfolioAccountType; import org.mifosplatform.portfolio.account.data.AccountTransferDTO; import org.mifosplatform.portfolio.account.data.PortfolioAccountData; -import org.mifosplatform.portfolio.account.domain.AccountTransfer; import org.mifosplatform.portfolio.account.domain.AccountTransferRepository; +import org.mifosplatform.portfolio.account.domain.AccountTransferTransaction; +import org.mifosplatform.portfolio.account.domain.AccountTransferType; import org.mifosplatform.portfolio.account.service.AccountAssociationsReadPlatformService; import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService; import org.mifosplatform.portfolio.account.service.AccountTransfersWritePlatformService; @@ -303,7 +304,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final PortfolioAccountData savingAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId); final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(actualDisbursementDate, entrySet.getValue(), PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, savingAccountData.accountId(), loanId, "Loan Charge Payment", - locale, fmt, null, null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getValue(), entrySet.getKey(), null); + locale, fmt, null, null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getValue(), entrySet.getKey(), null, AccountTransferType.CHARGE_PAYMENT.getValue(), null); this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); } @@ -460,7 +461,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(actualDisbursementDate, entrySet.getValue(), PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, savingAccountData.accountId(), loan.getId(), "Loan Charge Payment", locale, fmt, null, null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getValue(), - entrySet.getKey(), null); + entrySet.getKey(), null, AccountTransferType.CHARGE_PAYMENT.getValue(), null); this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); } } @@ -1206,7 +1207,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, amount, PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, portfolioAccountData.accountId(), loanId, "Loan Charge Payment", locale, fmt, null, null, - LoanTransactionType.CHARGE_PAYMENT.getValue(), loanChargeId, loanInstallmentNumber); + LoanTransactionType.CHARGE_PAYMENT.getValue(), loanChargeId, loanInstallmentNumber, AccountTransferType.CHARGE_PAYMENT.getValue(), null); this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); return new CommandProcessingResultBuilder() // @@ -1241,7 +1242,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf installmentChargeData.getAmountOutstanding(), PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, portfolioAccountData.accountId(), chargeData.getLoanId(), "Loan Charge Payment", null, null, null, null, LoanTransactionType.CHARGE_PAYMENT.getValue(), chargeData.getId(), - installmentChargeData.getInstallmentNumber()); + installmentChargeData.getInstallmentNumber(), AccountTransferType.CHARGE_PAYMENT.getValue(), null); transferFeeCharge(sb, accountTransferDTO); } } @@ -1251,7 +1252,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(new LocalDate(), chargeData.getAmountOutstanding(), PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, portfolioAccountData.accountId(), chargeData.getLoanId(), "Loan Charge Payment", null, null, null, null, - LoanTransactionType.CHARGE_PAYMENT.getValue(), chargeData.getId(), null); + LoanTransactionType.CHARGE_PAYMENT.getValue(), chargeData.getId(), null, AccountTransferType.CHARGE_PAYMENT.getValue(), null); transferFeeCharge(sb, accountTransferDTO); } } @@ -1916,7 +1917,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } private void updateLoanTransaction(final Long loanTransactionId, final LoanTransaction newLoanTransaction) { - final AccountTransfer transferTransaction = this.accountTransferRepository.findByToLoanTransactionId(loanTransactionId); + final AccountTransferTransaction transferTransaction = this.accountTransferRepository.findByToLoanTransactionId(loanTransactionId); if (transferTransaction != null) { transferTransaction.updateToLoanTransaction(newLoanTransaction); this.accountTransferRepository.save(transferTransaction); 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 9b8023462..4cb252e30 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 @@ -230,6 +230,10 @@ public class SavingsAccountAssembler { return account; } + public void setHelpers(final SavingsAccount account) { + account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper); + } + /** * Assembles a new {@link SavingsAccount} from JSON details passed in * request inheriting details where relevant from chosen 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 7bc672f3a..a987ca57f 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 @@ -638,8 +638,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead 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_savings_account_transfer fromtran on fromtran.from_savings_transaction_id = tr.id "); - sqlBuilder.append("left join m_savings_account_transfer totran on totran.to_savings_transaction_id = tr.id "); + 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 ");