diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index f982202d0..05feee8c6 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -581,61 +581,6 @@ - - -

Accounting

@@ -12892,7 +12837,6 @@ No Request Body:
- savingsaccounts_transactions_template  
@@ -12944,7 +12888,6 @@ No Request Body:
-  
@@ -13052,6 +12995,1457 @@ No Request Body:
+ + +  +
+
+

Account Transfers:

+

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

+

Note: At present only savings account to savings account transfers are supported.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Mandatory Parameters
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.
transferDate
The date of the transfer. Requires dateFormat and locale parameters.
transferAmount
The amount of the transfer. Requires locale parameter.
transferDescription
Description of the transfer itself.
+
+
+ +   +
+
+

Retrieve Account Transfer 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: +

+

+

Example Requests:

+
accounttransfers/template?fromAccountType=2&fromOfficeId=1
+
+
accounttransfers/template?fromAccountType=2&fromOfficeId=1&fromClientId=1
+
+
accounttransfers/template?fromClientId=1&fromAccountType=2&fromAccountId=1
+
+
+
+ GET https://Domain Name/api/v1/accounttransfers/template?fromAccountType=2&fromOfficeId=1 + + +{ + "transferAmount": 0, + "transferDate": [ + 2013, + 8, + 7 + ], + "fromOffice": { + "id": 1, + "name": "HO", + "nameDecorated": "HO", + "externalId": "1", + "openingDate": [ + 2009, + 1, + 1 + ], + "hierarchy": "." + }, + "toOffice": { + "id": 1, + "name": "HO", + "nameDecorated": "HO", + "externalId": "1", + "openingDate": [ + 2009, + 1, + 1 + ], + "hierarchy": "." + }, + "fromOfficeOptions": [ + { + "id": 1, + "name": "HO", + "nameDecorated": "HO" + }, + { + "id": 3, + "name": "branch 1", + "nameDecorated": "....branch 1" + } + ], + "fromClientOptions": [ + { + "id": 2, + "displayName": "headoffice-business", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + } + ], + "toOfficeOptions": [ + { + "id": 1, + "name": "HO", + "nameDecorated": "HO" + }, + { + "id": 3, + "name": "branch 1", + "nameDecorated": "....branch 1" + } + ], + "toClientOptions": [ + { + "id": 2, + "displayName": "headoffice-business", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + } + ] +} + +
+ +
+ GET https://Domain Name/api/v1/accounttransfers/template?fromAccountType=2&fromOfficeId=1&fromClientId=1 + + +{ + "transferAmount": 0, + "transferDate": [ + 2013, + 8, + 7 + ], + "fromOffice": { + "id": 3, + "name": "branch 1", + "nameDecorated": "....branch 1", + "externalId": "2", + "openingDate": [ + 2011, + 7, + 1 + ], + "hierarchy": ".3.", + "parentId": 1, + "parentName": "HO" + }, + "fromClient": { + "id": 1, + "accountNo": "000000001", + "status": { + "id": 300, + "code": "clientStatusType.active", + "value": "Active" + }, + "active": true, + "activationDate": [ + 2012, + 3, + 1 + ], + "fullname": "Small shop", + "displayName": "Small shop", + "officeId": 3, + "officeName": "branch 1", + "imagePresent": false, + "groups": [] + }, + "toOffice": { + "id": 3, + "name": "branch 1", + "nameDecorated": "....branch 1", + "externalId": "2", + "openingDate": [ + 2011, + 7, + 1 + ], + "hierarchy": ".3.", + "parentId": 1, + "parentName": "HO" + }, + "toClient": { + "id": 1, + "accountNo": "000000001", + "status": { + "id": 300, + "code": "clientStatusType.active", + "value": "Active" + }, + "active": true, + "activationDate": [ + 2012, + 3, + 1 + ], + "fullname": "Small shop", + "displayName": "Small shop", + "officeId": 3, + "officeName": "branch 1", + "imagePresent": false, + "groups": [] + }, + "fromOfficeOptions": [ + { + "id": 1, + "name": "HO", + "nameDecorated": "HO" + }, + { + "id": 3, + "name": "branch 1", + "nameDecorated": "....branch 1" + } + ], + "fromClientOptions": [ + { + "id": 1, + "displayName": "Small shop", + "officeId": 3, + "officeName": "branch 1", + "imagePresent": false + } + ], + "fromAccountOptions": [ + { + "id": 1, + "accountNo": "000000001", + "clientId": 1, + "clientName": "Small shop", + "savingsProductId": 1, + "savingsProductName": "Passbook", + "fieldOfficerId": 2, + "fieldOfficerName": "Clare, Claire", + "status": { + "id": 300, + "code": "savingsAccountStatusType.active", + "value": "Active", + "submittedAndPendingApproval": false, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": true, + "closed": false + }, + "timeline": { + "submittedOnDate": [ + 2013, + 3, + 1 + ], + "approvedOnDate": [ + 2013, + 3, + 1 + ], + "approvedByUsername": "mifos", + "approvedByFirstname": "App", + "approvedByLastname": "Administrator", + "activatedOnDate": [ + 2013, + 3, + 1 + ] + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "nominalAnnualInterestRate": 5, + "interestCompoundingPeriodType": { + "id": 1, + "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", + "value": "Daily" + }, + "interestPostingPeriodType": { + "id": 4, + "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", + "value": "Monthly" + }, + "interestCalculationType": { + "id": 1, + "code": "savingsInterestCalculationType.dailybalance", + "value": "Daily Balance" + }, + "interestCalculationDaysInYearType": { + "id": 365, + "code": "savingsInterestCalculationDaysInYearType.days365", + "value": "365 Days" + }, + "summary": { + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "totalDeposits": 1242, + "totalWithdrawals": 101, + "totalInterestEarned": 26.03, + "accountBalance": 1141 + } + }, + { + "id": 2, + "accountNo": "000000002", + "clientId": 1, + "clientName": "Small shop", + "savingsProductId": 1, + "savingsProductName": "Passbook", + "fieldOfficerId": 3, + "fieldOfficerName": "George, John", + "status": { + "id": 300, + "code": "savingsAccountStatusType.active", + "value": "Active", + "submittedAndPendingApproval": false, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": true, + "closed": false + }, + "timeline": { + "submittedOnDate": [ + 2013, + 6, + 1 + ], + "approvedOnDate": [ + 2013, + 6, + 1 + ], + "approvedByUsername": "mifos", + "approvedByFirstname": "App", + "approvedByLastname": "Administrator", + "activatedOnDate": [ + 2013, + 6, + 1 + ] + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "nominalAnnualInterestRate": 5, + "interestCompoundingPeriodType": { + "id": 1, + "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", + "value": "Daily" + }, + "interestPostingPeriodType": { + "id": 4, + "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", + "value": "Monthly" + }, + "interestCalculationType": { + "id": 1, + "code": "savingsInterestCalculationType.dailybalance", + "value": "Daily Balance" + }, + "interestCalculationDaysInYearType": { + "id": 365, + "code": "savingsInterestCalculationDaysInYearType.days365", + "value": "365 Days" + }, + "summary": { + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "totalDeposits": 101, + "totalWithdrawals": 42, + "totalInterestEarned": 0.58, + "accountBalance": 59 + } + } + ], + "toOfficeOptions": [ + { + "id": 1, + "name": "HO", + "nameDecorated": "HO" + }, + { + "id": 3, + "name": "branch 1", + "nameDecorated": "....branch 1" + } + ], + "toClientOptions": [ + { + "id": 1, + "displayName": "Small shop", + "officeId": 3, + "officeName": "branch 1", + "imagePresent": false + } + ], + "toAccountOptions": [ + { + "id": 1, + "accountNo": "000000001", + "clientId": 1, + "clientName": "Small shop", + "savingsProductId": 1, + "savingsProductName": "Passbook", + "fieldOfficerId": 2, + "fieldOfficerName": "Clare, Claire", + "status": { + "id": 300, + "code": "savingsAccountStatusType.active", + "value": "Active", + "submittedAndPendingApproval": false, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": true, + "closed": false + }, + "timeline": { + "submittedOnDate": [ + 2013, + 3, + 1 + ], + "approvedOnDate": [ + 2013, + 3, + 1 + ], + "approvedByUsername": "mifos", + "approvedByFirstname": "App", + "approvedByLastname": "Administrator", + "activatedOnDate": [ + 2013, + 3, + 1 + ] + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "nominalAnnualInterestRate": 5, + "interestCompoundingPeriodType": { + "id": 1, + "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", + "value": "Daily" + }, + "interestPostingPeriodType": { + "id": 4, + "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", + "value": "Monthly" + }, + "interestCalculationType": { + "id": 1, + "code": "savingsInterestCalculationType.dailybalance", + "value": "Daily Balance" + }, + "interestCalculationDaysInYearType": { + "id": 365, + "code": "savingsInterestCalculationDaysInYearType.days365", + "value": "365 Days" + }, + "summary": { + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "totalDeposits": 1242, + "totalWithdrawals": 101, + "totalInterestEarned": 26.03, + "accountBalance": 1141 + } + }, + { + "id": 2, + "accountNo": "000000002", + "clientId": 1, + "clientName": "Small shop", + "savingsProductId": 1, + "savingsProductName": "Passbook", + "fieldOfficerId": 3, + "fieldOfficerName": "George, John", + "status": { + "id": 300, + "code": "savingsAccountStatusType.active", + "value": "Active", + "submittedAndPendingApproval": false, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": true, + "closed": false + }, + "timeline": { + "submittedOnDate": [ + 2013, + 6, + 1 + ], + "approvedOnDate": [ + 2013, + 6, + 1 + ], + "approvedByUsername": "mifos", + "approvedByFirstname": "App", + "approvedByLastname": "Administrator", + "activatedOnDate": [ + 2013, + 6, + 1 + ] + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "nominalAnnualInterestRate": 5, + "interestCompoundingPeriodType": { + "id": 1, + "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", + "value": "Daily" + }, + "interestPostingPeriodType": { + "id": 4, + "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", + "value": "Monthly" + }, + "interestCalculationType": { + "id": 1, + "code": "savingsInterestCalculationType.dailybalance", + "value": "Daily Balance" + }, + "interestCalculationDaysInYearType": { + "id": 365, + "code": "savingsInterestCalculationDaysInYearType.days365", + "value": "365 Days" + }, + "summary": { + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "totalDeposits": 101, + "totalWithdrawals": 42, + "totalInterestEarned": 0.58, + "accountBalance": 59 + } + } + ] +} + +
+ +
+ GET https://Domain Name/api/v1/accounttransfers/template?fromClientId=1&fromAccountType=2&fromAccountId=1 + + +{ + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "transferAmount": 0, + "transferDate": [ + 2013, + 8, + 7 + ], + "fromOffice": { + "id": 3, + "name": "branch 1", + "nameDecorated": "....branch 1", + "externalId": "2", + "openingDate": [ + 2011, + 7, + 1 + ], + "hierarchy": ".3.", + "parentId": 1, + "parentName": "HO" + }, + "fromClient": { + "id": 1, + "accountNo": "000000001", + "status": { + "id": 300, + "code": "clientStatusType.active", + "value": "Active" + }, + "active": true, + "activationDate": [ + 2012, + 3, + 1 + ], + "fullname": "Small shop", + "displayName": "Small shop", + "officeId": 3, + "officeName": "branch 1", + "imagePresent": false, + "groups": [] + }, + "fromAccount": { + "id": 1, + "accountNo": "000000001", + "clientId": 1, + "clientName": "Small shop", + "savingsProductId": 1, + "savingsProductName": "Passbook", + "fieldOfficerId": 2, + "fieldOfficerName": "Clare, Claire", + "status": { + "id": 300, + "code": "savingsAccountStatusType.active", + "value": "Active", + "submittedAndPendingApproval": false, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": true, + "closed": false + }, + "timeline": { + "submittedOnDate": [ + 2013, + 3, + 1 + ], + "approvedOnDate": [ + 2013, + 3, + 1 + ], + "approvedByUsername": "mifos", + "approvedByFirstname": "App", + "approvedByLastname": "Administrator", + "activatedOnDate": [ + 2013, + 3, + 1 + ] + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "nominalAnnualInterestRate": 5, + "interestCompoundingPeriodType": { + "id": 1, + "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", + "value": "Daily" + }, + "interestPostingPeriodType": { + "id": 4, + "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", + "value": "Monthly" + }, + "interestCalculationType": { + "id": 1, + "code": "savingsInterestCalculationType.dailybalance", + "value": "Daily Balance" + }, + "interestCalculationDaysInYearType": { + "id": 365, + "code": "savingsInterestCalculationDaysInYearType.days365", + "value": "365 Days" + }, + "summary": { + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "totalDeposits": 1242, + "totalWithdrawals": 101, + "totalInterestEarned": 26.03, + "accountBalance": 1141 + } + }, + "toOffice": { + "id": 3, + "name": "branch 1", + "nameDecorated": "....branch 1", + "externalId": "2", + "openingDate": [ + 2011, + 7, + 1 + ], + "hierarchy": ".3.", + "parentId": 1, + "parentName": "HO" + }, + "toClient": { + "id": 1, + "accountNo": "000000001", + "status": { + "id": 300, + "code": "clientStatusType.active", + "value": "Active" + }, + "active": true, + "activationDate": [ + 2012, + 3, + 1 + ], + "fullname": "Small shop", + "displayName": "Small shop", + "officeId": 3, + "officeName": "branch 1", + "imagePresent": false, + "groups": [] + }, + "fromOfficeOptions": [ + { + "id": 1, + "name": "HO", + "nameDecorated": "HO" + }, + { + "id": 3, + "name": "branch 1", + "nameDecorated": "....branch 1" + } + ], + "fromClientOptions": [ + { + "id": 1, + "displayName": "Small shop", + "officeId": 3, + "officeName": "branch 1", + "imagePresent": false + } + ], + "fromAccountOptions": [ + { + "id": 1, + "accountNo": "000000001", + "clientId": 1, + "clientName": "Small shop", + "savingsProductId": 1, + "savingsProductName": "Passbook", + "fieldOfficerId": 2, + "fieldOfficerName": "Clare, Claire", + "status": { + "id": 300, + "code": "savingsAccountStatusType.active", + "value": "Active", + "submittedAndPendingApproval": false, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": true, + "closed": false + }, + "timeline": { + "submittedOnDate": [ + 2013, + 3, + 1 + ], + "approvedOnDate": [ + 2013, + 3, + 1 + ], + "approvedByUsername": "mifos", + "approvedByFirstname": "App", + "approvedByLastname": "Administrator", + "activatedOnDate": [ + 2013, + 3, + 1 + ] + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "nominalAnnualInterestRate": 5, + "interestCompoundingPeriodType": { + "id": 1, + "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", + "value": "Daily" + }, + "interestPostingPeriodType": { + "id": 4, + "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", + "value": "Monthly" + }, + "interestCalculationType": { + "id": 1, + "code": "savingsInterestCalculationType.dailybalance", + "value": "Daily Balance" + }, + "interestCalculationDaysInYearType": { + "id": 365, + "code": "savingsInterestCalculationDaysInYearType.days365", + "value": "365 Days" + }, + "summary": { + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "totalDeposits": 1242, + "totalWithdrawals": 101, + "totalInterestEarned": 26.03, + "accountBalance": 1141 + } + }, + { + "id": 2, + "accountNo": "000000002", + "clientId": 1, + "clientName": "Small shop", + "savingsProductId": 1, + "savingsProductName": "Passbook", + "fieldOfficerId": 3, + "fieldOfficerName": "George, John", + "status": { + "id": 300, + "code": "savingsAccountStatusType.active", + "value": "Active", + "submittedAndPendingApproval": false, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": true, + "closed": false + }, + "timeline": { + "submittedOnDate": [ + 2013, + 6, + 1 + ], + "approvedOnDate": [ + 2013, + 6, + 1 + ], + "approvedByUsername": "mifos", + "approvedByFirstname": "App", + "approvedByLastname": "Administrator", + "activatedOnDate": [ + 2013, + 6, + 1 + ] + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "nominalAnnualInterestRate": 5, + "interestCompoundingPeriodType": { + "id": 1, + "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", + "value": "Daily" + }, + "interestPostingPeriodType": { + "id": 4, + "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", + "value": "Monthly" + }, + "interestCalculationType": { + "id": 1, + "code": "savingsInterestCalculationType.dailybalance", + "value": "Daily Balance" + }, + "interestCalculationDaysInYearType": { + "id": 365, + "code": "savingsInterestCalculationDaysInYearType.days365", + "value": "365 Days" + }, + "summary": { + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "totalDeposits": 101, + "totalWithdrawals": 42, + "totalInterestEarned": 0.58, + "accountBalance": 59 + } + } + ], + "toOfficeOptions": [ + { + "id": 1, + "name": "HO", + "nameDecorated": "HO" + }, + { + "id": 3, + "name": "branch 1", + "nameDecorated": "....branch 1" + } + ], + "toClientOptions": [ + { + "id": 1, + "displayName": "Small shop", + "officeId": 3, + "officeName": "branch 1", + "imagePresent": false + } + ], + "toAccountOptions": [ + { + "id": 2, + "accountNo": "000000002", + "clientId": 1, + "clientName": "Small shop", + "savingsProductId": 1, + "savingsProductName": "Passbook", + "fieldOfficerId": 3, + "fieldOfficerName": "George, John", + "status": { + "id": 300, + "code": "savingsAccountStatusType.active", + "value": "Active", + "submittedAndPendingApproval": false, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": true, + "closed": false + }, + "timeline": { + "submittedOnDate": [ + 2013, + 6, + 1 + ], + "approvedOnDate": [ + 2013, + 6, + 1 + ], + "approvedByUsername": "mifos", + "approvedByFirstname": "App", + "approvedByLastname": "Administrator", + "activatedOnDate": [ + 2013, + 6, + 1 + ] + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "nominalAnnualInterestRate": 5, + "interestCompoundingPeriodType": { + "id": 1, + "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", + "value": "Daily" + }, + "interestPostingPeriodType": { + "id": 4, + "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", + "value": "Monthly" + }, + "interestCalculationType": { + "id": 1, + "code": "savingsInterestCalculationType.dailybalance", + "value": "Daily Balance" + }, + "interestCalculationDaysInYearType": { + "id": 365, + "code": "savingsInterestCalculationDaysInYearType.days365", + "value": "365 Days" + }, + "summary": { + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "totalDeposits": 101, + "totalWithdrawals": 42, + "totalInterestEarned": 0.58, + "accountBalance": 59 + } + } + ] +} + +
+ +
+ +   +
+
+

Create new Transfer

+

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

+
+
+ POST https://Domain Name/api/v1/accounttransfers + POST accounttransfers/ +Content-Type: application/json +No Request Body: +{ +"fromOfficeId": 1, +"fromClientId": 1, +"fromAccountType": 2, +"fromAccountId": 1, +"toOfficeId": 1, +"toClientId": 1, +"toAccountType": 2, +"toAccountId": 2, +"dateFormat": "dd MMMM yyyy", +"locale": "en", +"transferDate": "01 August 2011", +"transferAmount": "112.45", +"transferDescription": "A description of the transfer" +} + + + +
+
+ +   +
+
+

List account transfers

+

Example Requests:

+
accounttransfers
+
+
+
+ GET https://Domain Name/api/v1/accounttransfers + +{ + "totalFilteredRecords": 5, + "pageItems": [ + { + "id": 1, + "reversed": false, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "transferAmount": 25, + "transferDate": [ + 2013, + 6, + 8 + ], + "transferDescription": "another transfer", + "fromOffice": { + "id": 1, + "name": "HO" + }, + "fromClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "fromAccount": { + "id": 1, + "accountNo": "000000001" + }, + "toOffice": { + "id": 1, + "name": "HO" + }, + "toClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "toAccount": { + "id": 2, + "accountNo": "000000002" + } + }, + { + "id": 2, + "reversed": false, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "transferAmount": 13, + "transferDate": [ + 2013, + 6, + 15 + ], + "transferDescription": "T3", + "fromOffice": { + "id": 1, + "name": "HO" + }, + "fromClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "fromAccount": { + "id": 1, + "accountNo": "000000001" + }, + "toOffice": { + "id": 1, + "name": "HO" + }, + "toClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "toAccount": { + "id": 2, + "accountNo": "000000002" + } + }, + { + "id": 3, + "reversed": false, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "transferAmount": 17, + "transferDate": [ + 2013, + 6, + 22 + ], + "transferDescription": "T4", + "fromOffice": { + "id": 1, + "name": "HO" + }, + "fromClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "fromAccount": { + "id": 1, + "accountNo": "000000001" + }, + "toOffice": { + "id": 1, + "name": "HO" + }, + "toClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "toAccount": { + "id": 2, + "accountNo": "000000002" + } + }, + { + "id": 4, + "reversed": false, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "transferAmount": 21, + "transferDate": [ + 2013, + 6, + 29 + ], + "transferDescription": "T5", + "fromOffice": { + "id": 1, + "name": "HO" + }, + "fromClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "fromAccount": { + "id": 1, + "accountNo": "000000001" + }, + "toOffice": { + "id": 1, + "name": "HO" + }, + "toClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "toAccount": { + "id": 2, + "accountNo": "000000002" + } + }, + { + "id": 5, + "reversed": false, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "transferAmount": 42, + "transferDate": [ + 2013, + 7, + 5 + ], + "transferDescription": "pay back loan", + "fromOffice": { + "id": 1, + "name": "HO" + }, + "fromClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "fromAccount": { + "id": 2, + "accountNo": "000000002" + }, + "toOffice": { + "id": 1, + "name": "HO" + }, + "toClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "toAccount": { + "id": 1, + "accountNo": "000000001" + } + } + ] +} + +
+
+ +   +
+
+

Retrieve account transfer:

+

Example Requests :

+
accounttransfers/1
+
+
+
+ GET https://DomainName/api/v1/accounttransfers/{transferId} + +{ + "id": 1, + "reversed": false, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "transferAmount": 25, + "transferDate": [ + 2013, + 6, + 8 + ], + "transferDescription": "another transfer", + "fromOffice": { + "id": 1, + "name": "HO" + }, + "fromClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "fromAccount": { + "id": 1, + "accountNo": "000000001" + }, + "toOffice": { + "id": 1, + "name": "HO" + }, + "toClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "toAccount": { + "id": 2, + "accountNo": "000000002" + } +} + +
+
+   @@ -18281,14 +19675,6 @@ GET https://DomainName/api/v1/search?query=000000111 - - - savingsaccounts/{accountId}/transactions/template - - Retrieve savings account transaction template - - - Savings Transactions savingsaccounts/{accountId}/transactions?command=deposit @@ -18312,7 +19698,39 @@ GET https://DomainName/api/v1/search?query=000000111 - + + + + savingsaccounts/{accountId}/transactions/template + + Retrieve savings account transaction template + + + + + Account Transfers + accounttransfers/ + Create new Transfer + List Transfers + + + + + + accounttransfers/template + + Retrieve Template + + + + + + accounttransfers/{transferId} + + Retrieve Transfer + + + diff --git a/mifosng-db/migrations/core_db/V97__savings_accounts_transfers_table.sql b/mifosng-db/migrations/core_db/V97__savings_accounts_transfers_table.sql new file mode 100644 index 000000000..425b17b4a --- /dev/null +++ b/mifosng-db/migrations/core_db/V97__savings_accounts_transfers_table.sql @@ -0,0 +1,56 @@ +INSERT INTO `m_permission` +(`grouping`,`code`,`entity_name`,`action_name`,`can_maker_checker`) +VALUES +('transaction_savings', 'READ_ACCOUNTTRANSFER', 'ACCOUNTTRANSFER', 'READ', 0), +('transaction_savings', 'CREATE_ACCOUNTTRANSFER', 'ACCOUNTTRANSFER', 'CREATE', 1), +('transaction_savings', 'CREATE_ACCOUNTTRANSFER_CHECKER', 'ACCOUNTTRANSFER', 'CREATE', 0); + + +DROP TABLE IF EXISTS `m_savings_account_transfer`; + +CREATE TABLE `m_savings_account_transfer` ( + `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) NOT NULL, + `to_client_id` bigint(20) NOT NULL, + `from_savings_account_id` bigint(20) DEFAULT NULL, + `to_savings_account_id` bigint(20) DEFAULT NULL, + `from_loan_account_id` bigint(20) DEFAULT NULL, + `to_loan_account_id` bigint(20) DEFAULT NULL, + `from_savings_transaction_id` bigint(20) DEFAULT NULL, + `from_loan_transaction_id` bigint(20) DEFAULT NULL, + `to_savings_transaction_id` bigint(20) DEFAULT NULL, + `to_loan_transaction_id` bigint(20) 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, + `amount` decimal(19,6) NOT NULL, + `description` varchar(200) NOT NULL, + PRIMARY KEY (`id`), + KEY `FKTRAN000000001` (`from_office_id`), + KEY `FKTRAN000000002` (`from_client_id`), + KEY `FKTRAN000000003` (`from_savings_account_id`), + KEY `FKTRAN000000004` (`to_office_id`), + KEY `FKTRAN000000005` (`to_client_id`), + KEY `FKTRAN000000006` (`to_savings_account_id`), + KEY `FKTRAN000000007` (`to_loan_account_id`), + KEY `FKTRAN000000008` (`from_savings_transaction_id`), + KEY `FKTRAN000000009` (`to_savings_transaction_id`), + KEY `FKTRAN000000010` (`to_loan_transaction_id`), + KEY `FKTRAN000000011` (`from_loan_account_id`), + KEY `FKTRAN000000012` (`from_loan_transaction_id`), + CONSTRAINT `FKTRAN000000001` FOREIGN KEY (`from_office_id`) REFERENCES `m_office` (`id`), + CONSTRAINT `FKTRAN000000002` FOREIGN KEY (`from_client_id`) REFERENCES `m_client` (`id`), + CONSTRAINT `FKTRAN000000003` FOREIGN KEY (`from_savings_account_id`) REFERENCES `m_savings_account` (`id`), + CONSTRAINT `FKTRAN000000004` FOREIGN KEY (`to_office_id`) REFERENCES `m_office` (`id`), + CONSTRAINT `FKTRAN000000005` FOREIGN KEY (`to_client_id`) REFERENCES `m_client` (`id`), + CONSTRAINT `FKTRAN000000006` FOREIGN KEY (`to_savings_account_id`) REFERENCES `m_savings_account` (`id`), + CONSTRAINT `FKTRAN000000007` FOREIGN KEY (`to_loan_account_id`) REFERENCES `m_loan` (`id`), + CONSTRAINT `FKTRAN000000008` FOREIGN KEY (`from_savings_transaction_id`) REFERENCES `m_savings_account_transaction` (`id`), + CONSTRAINT `FKTRAN000000009` FOREIGN KEY (`to_savings_transaction_id`) REFERENCES `m_savings_account_transaction` (`id`), + CONSTRAINT `FKTRAN000000010` FOREIGN KEY (`to_loan_transaction_id`) REFERENCES `m_loan_transaction` (`id`), + CONSTRAINT `FKTRAN000000011` FOREIGN KEY (`from_loan_account_id`) REFERENCES `m_loan` (`id`), + CONSTRAINT `FKTRAN000000012` FOREIGN KEY (`from_loan_transaction_id`) REFERENCES `m_loan_transaction` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; \ No newline at end of file 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 3385bdee5..3e973eddc 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 @@ -563,4 +563,8 @@ public class CommandWrapper { public boolean isSchedulerResource() { return this.entityName.equalsIgnoreCase("SCHEDULER"); } + + public boolean isAccountTransferResource() { + return this.entityName.equalsIgnoreCase("ACCOUNTTRANSFER"); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java index 6da48c263..445fd23c1 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 @@ -808,6 +808,14 @@ public class CommandWrapperBuilder { return this; } + public CommandWrapperBuilder createAccountTransfer() { + this.actionName = "CREATE"; + this.entityName = "ACCOUNTTRANSFER"; + this.entityId = null; + this.href = "/accounttransfers"; + return this; + } + public CommandWrapperBuilder savingsAccountDeposit(final Long accountId) { this.actionName = "DEPOSIT"; this.entityName = "SAVINGSACCOUNT"; 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 f8f3614b2..eabf31970 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 @@ -105,7 +105,14 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer private NewCommandSourceHandler findCommandHandler(final CommandWrapper wrapper) { NewCommandSourceHandler handler = null; - if (wrapper.isConfigurationResource()) { + if (wrapper.isAccountTransferResource()) { + if (wrapper.isCreate()) { + handler = this.applicationContext.getBean("createAccountTransferCommandHandler", NewCommandSourceHandler.class); + } else { + throw new UnsupportedCommandException(wrapper.commandName()); + } + } + else if (wrapper.isConfigurationResource()) { handler = this.applicationContext.getBean("updateGlobalConfigurationCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isDatatableResource()) { if (wrapper.isCreateDatatable()) { 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 cca8704cd..976003921 100644 --- 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 @@ -4,7 +4,7 @@ public enum JobName { UPDATE_LOAN_SUMMARY("Update loan Summary"), UPDATE_LOAN_ARREARS_AGEING("Update Loan Arrears Ageing"), UPDATE_LOAN_PAID_IN_ADVANCE( "Update Loan Paid In Advance"), APPLY_ANNUAL_FEE_FOR_SAVINGS("Apply Annual Fee For Savings"), APPLY_HOLIDAYS_TO_LOANS( - "Apply Holidays To Loans"),POST_INTEREST_FOR_SAVINGS("Post Interest For Savings"); + "Apply Holidays To Loans"), POST_INTEREST_FOR_SAVINGS("Post Interest For Savings"); private final String name; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/AccountTransfersApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/AccountTransfersApiConstants.java new file mode 100644 index 000000000..03959d511 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/AccountTransfersApiConstants.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.accounttransfers; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.mifosplatform.portfolio.accounttransfers.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/accounttransfers/api/AccountTransfersApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/api/AccountTransfersApiResource.java new file mode 100644 index 000000000..37772bf38 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/api/AccountTransfersApiResource.java @@ -0,0 +1,122 @@ +/** + * 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.accounttransfers.api; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; + +import org.mifosplatform.commands.domain.CommandWrapper; +import org.mifosplatform.commands.service.CommandWrapperBuilder; +import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; +import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.mifosplatform.infrastructure.core.service.Page; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants; +import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData; +import org.mifosplatform.portfolio.accounttransfers.service.AccountTransfersReadPlatformService; +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("/accounttransfers") +@Component +@Scope("singleton") +public class AccountTransfersApiResource { + + private final PlatformSecurityContext context; + private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final ApiRequestParameterHelper apiRequestParameterHelper; + private final AccountTransfersReadPlatformService accountTransfersReadPlatformService; + + @Autowired + public AccountTransfersApiResource(final PlatformSecurityContext context, + final DefaultToApiJsonSerializer toApiJsonSerializer, + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, + final ApiRequestParameterHelper apiRequestParameterHelper, + final AccountTransfersReadPlatformService accountTransfersReadPlatformService) { + this.context = context; + this.toApiJsonSerializer = toApiJsonSerializer; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + this.apiRequestParameterHelper = apiRequestParameterHelper; + 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, + @Context final UriInfo uriInfo) { + + context.authenticatedUser().validateHasReadPermission(AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME); + + AccountTransferData transferData = this.accountTransfersReadPlatformService.retrieveTemplate(fromOfficeId, fromClientId, + fromAccountId, fromAccountType, toOfficeId, toClientId, toAccountId, toAccountType); + + final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, transferData, AccountTransfersApiConstants.RESPONSE_DATA_PARAMETERS); + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String create(final String apiRequestBodyAsJson) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().createAccountTransfer().withJson(apiRequestBodyAsJson).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + @GET + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveAll(@Context final UriInfo uriInfo, @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) { + + context.authenticatedUser().validateHasReadPermission(AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME); + + final SearchParameters searchParameters = SearchParameters.forLoans(sqlSearch, externalId, offset, limit, orderBy, sortOrder); + + final Page transfers = this.accountTransfersReadPlatformService.retrieveAll(searchParameters); + + final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, transfers, AccountTransfersApiConstants.RESPONSE_DATA_PARAMETERS); + } + + @GET + @Path("{transferId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveOne(@PathParam("transferId") final Long transferId, @Context final UriInfo uriInfo) { + + context.authenticatedUser().validateHasReadPermission(AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME); + + AccountTransferData transfer = this.accountTransfersReadPlatformService.retrieveOne(transferId); + + final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, transfer, AccountTransfersApiConstants.RESPONSE_DATA_PARAMETERS); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/data/AccountTransferData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/data/AccountTransferData.java new file mode 100644 index 000000000..e2fd10334 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/data/AccountTransferData.java @@ -0,0 +1,108 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.accounttransfers.data; + +import java.math.BigDecimal; +import java.util.Collection; + +import org.joda.time.LocalDate; +import org.mifosplatform.organisation.monetary.data.CurrencyData; +import org.mifosplatform.organisation.office.data.OfficeData; +import org.mifosplatform.portfolio.client.data.ClientData; +import org.mifosplatform.portfolio.savings.data.SavingsAccountData; + +/** + * Immutable data object representing a savings account. + */ +@SuppressWarnings("unused") +public class AccountTransferData { + + private final Long id; + private final Boolean reversed; + private final CurrencyData currency; + private final BigDecimal transferAmount; + private final LocalDate transferDate; + private final String transferDescription; + private final OfficeData fromOffice; + private final ClientData fromClient; + private final SavingsAccountData fromAccount; + private final OfficeData toOffice; + private final ClientData toClient; + private final SavingsAccountData toAccount; + + // template + private final Collection fromOfficeOptions; + private final Collection fromClientOptions; + private final Collection fromAccountOptions; + private final Collection toOfficeOptions; + private final Collection toClientOptions; + private final Collection toAccountOptions; + + public static AccountTransferData template(final OfficeData fromOffice, final ClientData fromClient, + final SavingsAccountData fromAccount, final LocalDate transferDate, final OfficeData toOffice, final ClientData toClient, + final SavingsAccountData toAccount, final Collection fromOfficeOptions, + final Collection fromClientOptions, final Collection fromAccountOptions, + final Collection toOfficeOptions, final Collection toClientOptions, + final Collection toAccountOptions) { + final Long id = null; + CurrencyData currency = null; + if (fromAccount != null) { + currency = fromAccount.currency(); + } + + final BigDecimal transferAmount = BigDecimal.ZERO; + final String transferDescription = null; + final Boolean reversed = null; + return new AccountTransferData(id, reversed, fromOffice, fromClient, fromAccount, currency, transferAmount, transferDate, + transferDescription, toOffice, toClient, toAccount, fromOfficeOptions, fromClientOptions, fromAccountOptions, + toOfficeOptions, toClientOptions, toAccountOptions); + } + + public static AccountTransferData instance(final Long id, final Boolean reversed, final LocalDate transferDate, + final CurrencyData currency, final BigDecimal transferAmount, final String transferDescription, final OfficeData fromOffice, + final OfficeData toOffice, final ClientData fromClient, final ClientData toClient, final SavingsAccountData fromSavingsAccount, + final SavingsAccountData toSavingsAccount) { + + return new AccountTransferData(id, reversed, fromOffice, fromClient, fromSavingsAccount, currency, transferAmount, transferDate, + transferDescription, toOffice, toClient, toSavingsAccount, null, null, null, null, null, null); + } + + public static AccountTransferData transferBasicDetails(final Long id, final CurrencyData currency, final BigDecimal transferAmount, + final LocalDate transferDate, final String description, final Boolean reversed) { + + return new AccountTransferData(id, reversed, null, null, null, currency, transferAmount, transferDate, description, null, null, + null, null, null, null, null, null, null); + } + + private AccountTransferData(final Long id, final Boolean reversed, final OfficeData fromOffice, final ClientData fromClient, + final SavingsAccountData fromAccount, final CurrencyData currency, final BigDecimal transferAmount, + final LocalDate transferDate, final String transferDescription, final OfficeData toOffice, final ClientData toClient, + final SavingsAccountData toAccount, final Collection fromOfficeOptions, + final Collection fromClientOptions, final Collection fromAccountOptions, + final Collection toOfficeOptions, final Collection toClientOptions, + final Collection toAccountOptions) { + this.id = id; + this.reversed = reversed; + this.fromOffice = fromOffice; + this.fromClient = fromClient; + this.fromAccount = fromAccount; + this.toOffice = toOffice; + this.toClient = toClient; + this.toAccount = toAccount; + + this.currency = currency; + this.transferAmount = transferAmount; + this.transferDate = transferDate; + this.transferDescription = transferDescription; + + this.fromOfficeOptions = fromOfficeOptions; + this.fromClientOptions = fromClientOptions; + this.fromAccountOptions = fromAccountOptions; + this.toOfficeOptions = toOfficeOptions; + this.toClientOptions = toClientOptions; + this.toAccountOptions = toAccountOptions; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/data/AccountTransfersDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/data/AccountTransfersDataValidator.java new file mode 100644 index 000000000..0de979eb2 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/data/AccountTransfersDataValidator.java @@ -0,0 +1,108 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.accounttransfers.data; + +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.REQUEST_DATA_PARAMETERS; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferDescriptionParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromAccountIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromAccountTypeParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromClientIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromOfficeIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toAccountIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toAccountTypeParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toClientIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toOfficeIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferAmountParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferDateParamName; + +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.google.gson.JsonElement; +import com.google.gson.reflect.TypeToken; + +@Component +public class AccountTransfersDataValidator { + + private final FromJsonHelper fromApiJsonHelper; + + @Autowired + public AccountTransfersDataValidator(final FromJsonHelper fromApiJsonHelper) { + this.fromApiJsonHelper = fromApiJsonHelper; + } + + public void validate(final JsonCommand command) { + + final String json = command.json(); + + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(ACCOUNT_TRANSFER_RESOURCE_NAME); + + final JsonElement element = command.parsedJson(); + + final Long fromOfficeId = fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); + baseDataValidator.reset().parameter(fromOfficeIdParamName).value(fromOfficeId).notNull().integerGreaterThanZero(); + + final Long fromClientId = fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); + baseDataValidator.reset().parameter(fromClientIdParamName).value(fromClientId).notNull().integerGreaterThanZero(); + + final Long fromAccountId = fromApiJsonHelper.extractLongNamed(fromAccountIdParamName, element); + baseDataValidator.reset().parameter(fromAccountIdParamName).value(fromAccountId).notNull().integerGreaterThanZero(); + + final Integer fromAccountType = fromApiJsonHelper.extractIntegerSansLocaleNamed(fromAccountTypeParamName, element); + baseDataValidator.reset().parameter(fromAccountTypeParamName).value(fromAccountType) + .isOneOfTheseValues(Integer.valueOf(1), Integer.valueOf(2)); + + final Long toOfficeId = fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); + baseDataValidator.reset().parameter(toOfficeIdParamName).value(toOfficeId).notNull().integerGreaterThanZero(); + + final Long toClientId = fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); + baseDataValidator.reset().parameter(toClientIdParamName).value(toClientId).notNull().integerGreaterThanZero(); + + final Long toAccountId = fromApiJsonHelper.extractLongNamed(toAccountIdParamName, element); + baseDataValidator.reset().parameter(toAccountIdParamName).value(toAccountId).notNull().integerGreaterThanZero(); + + final Integer toAccountType = fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element); + baseDataValidator.reset().parameter(toAccountTypeParamName).value(toAccountType) + .isOneOfTheseValues(Integer.valueOf(1), Integer.valueOf(2)); + + final LocalDate transactionDate = fromApiJsonHelper.extractLocalDateNamed(transferDateParamName, element); + baseDataValidator.reset().parameter(transferDateParamName).value(transactionDate).notNull(); + + final BigDecimal transactionAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(transferAmountParamName, element); + baseDataValidator.reset().parameter(transferAmountParamName).value(transactionAmount).notNull().positiveAmount(); + + final String transactionDescription = fromApiJsonHelper.extractStringNamed(transferDescriptionParamName, element); + baseDataValidator.reset().parameter(transferDescriptionParamName).value(transactionDescription).notBlank().notExceedingLengthOf(200); + + 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/accounttransfers/domain/AccountTransfer.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransfer.java new file mode 100644 index 000000000..b31ba4cbc --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransfer.java @@ -0,0 +1,111 @@ +/** + * 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.accounttransfers.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.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 toAccount; + + @ManyToOne + @JoinColumn(name = "from_savings_transaction_id", nullable = true) + private SavingsAccountTransaction fromTransaction; + + @ManyToOne + @JoinColumn(name = "to_savings_transaction_id", nullable = true) + private SavingsAccountTransaction toSavingsTransaction; + + @Column(name = "is_reversed", nullable = false) + private final 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, toOffice, toClient, toSavingsAccount, withdrawal, deposit, + transactionDate, transactionAmount, description); + } + + protected AccountTransfer() { + // + } + + private AccountTransfer(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) { + this.fromOffice = fromOffice; + this.fromClient = fromClient; + this.fromAccount = fromSavingsAccount; + this.toOffice = toOffice; + this.toClient = toClient; + this.toAccount = toSavingsAccount; + this.fromTransaction = withdrawal; + this.toSavingsTransaction = deposit; + this.date = transactionDate.toDate(); + this.currency = transactionAmount.getCurrency(); + this.amount = transactionAmount.getAmountDefaultedToNullIfZero(); + this.description = description; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransferAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransferAssembler.java new file mode 100644 index 000000000..6ed7f155d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransferAssembler.java @@ -0,0 +1,85 @@ +/** + * 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.accounttransfers.domain; + +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferDescriptionParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromAccountIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromClientIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromOfficeIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toAccountIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toClientIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toOfficeIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferAmountParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferDateParamName; + +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.client.domain.Client; +import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; +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; + + @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 AccountTransfer assembleSavingsToSavingsTransfer(final JsonCommand command, final SavingsAccountTransaction withdrawal, + final SavingsAccountTransaction deposit) { + + final JsonElement element = command.parsedJson(); + + final Long fromOfficeId = fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); + final Office fromOffice = this.officeRepository.findOne(fromOfficeId); + + final Long fromClientId = 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 = fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); + final Office toOffice = this.officeRepository.findOne(toOfficeId); + + final Long toClientId = 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 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, + withdrawal, deposit, transactionDate, transactionMonetaryAmount, description); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransferRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransferRepository.java new file mode 100644 index 000000000..652565cd9 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransferRepository.java @@ -0,0 +1,13 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.accounttransfers.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface AccountTransferRepository extends JpaRepository, JpaSpecificationExecutor { + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/exception/AccountTransferNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/exception/AccountTransferNotFoundException.java new file mode 100644 index 000000000..e7168bfcc --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/exception/AccountTransferNotFoundException.java @@ -0,0 +1,15 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.accounttransfers.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +public class AccountTransferNotFoundException extends AbstractPlatformResourceNotFoundException { + + public AccountTransferNotFoundException(final Long id) { + super("error.msg.accounttransfer.id.invalid", "Account transfer with identifier " + id + " does not exist", id); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/handler/CreateAccountTransferCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/handler/CreateAccountTransferCommandHandler.java new file mode 100644 index 000000000..10da9690d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/handler/CreateAccountTransferCommandHandler.java @@ -0,0 +1,32 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.accounttransfers.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.accounttransfers.service.AccountTransfersWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class CreateAccountTransferCommandHandler implements NewCommandSourceHandler { + + private final AccountTransfersWritePlatformService writePlatformService; + + @Autowired + public CreateAccountTransferCommandHandler(final AccountTransfersWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + + return writePlatformService.create(command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformService.java new file mode 100644 index 000000000..945f09d9c --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformService.java @@ -0,0 +1,20 @@ +/** + * 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.accounttransfers.service; + +import org.mifosplatform.infrastructure.core.service.Page; +import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData; +import org.mifosplatform.portfolio.group.service.SearchParameters; + +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); + + AccountTransferData retrieveOne(Long transferId); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformServiceImpl.java new file mode 100644 index 000000000..7a19cac0a --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformServiceImpl.java @@ -0,0 +1,291 @@ +/** + * 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.accounttransfers.service; + +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 org.joda.time.LocalDate; +import org.mifosplatform.commands.exception.UnsupportedCommandException; +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.monetary.data.CurrencyData; +import org.mifosplatform.organisation.office.data.OfficeData; +import org.mifosplatform.organisation.office.service.OfficeReadPlatformService; +import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData; +import org.mifosplatform.portfolio.accounttransfers.exception.AccountTransferNotFoundException; +import org.mifosplatform.portfolio.client.data.ClientData; +import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; +import org.mifosplatform.portfolio.group.service.SearchParameters; +import org.mifosplatform.portfolio.savings.data.SavingsAccountData; +import org.mifosplatform.portfolio.savings.service.SavingsAccountReadPlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class AccountTransfersReadPlatformServiceImpl implements AccountTransfersReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + private final ClientReadPlatformService clientReadPlatformService; + private final OfficeReadPlatformService officeReadPlatformService; + private final SavingsAccountReadPlatformService savingsAccountReadPlatformService; + + // mapper + private final AccountTransfersMapper accountTransfersMapper; + + // pagination + private final PaginationHelper paginationHelper = new PaginationHelper(); + + @Autowired + public AccountTransfersReadPlatformServiceImpl(final RoutingDataSource dataSource, + final ClientReadPlatformService clientReadPlatformService, final OfficeReadPlatformService officeReadPlatformService, + final SavingsAccountReadPlatformService savingsAccountReadPlatformService) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.clientReadPlatformService = clientReadPlatformService; + this.officeReadPlatformService = officeReadPlatformService; + this.savingsAccountReadPlatformService = savingsAccountReadPlatformService; + + this.accountTransfersMapper = new AccountTransfersMapper(); + } + + @Override + public AccountTransferData 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 mostRelevantFromAccountType = fromAccountType; + if (mostRelevantFromAccountType == null) { + mostRelevantFromAccountType = 2; // savings + } + + Integer mostRelevantToAccountType = toAccountType; + if (mostRelevantToAccountType == null) { + mostRelevantToAccountType = 2; // savings + } + // check for now that only savings account type is passed and used. + if (mostRelevantFromAccountType != 2) { throw new UnsupportedCommandException("transferFromLoanAccount"); } + + // from settings + OfficeData fromOffice = null; + ClientData fromClient = null; + SavingsAccountData fromAccount = null; + + OfficeData toOffice = null; + ClientData toClient = null; + SavingsAccountData toAccount = null; + + // template + Collection fromAccountOptions = null; + Collection toAccountOptions = null; + + Long mostRelevantFromOfficeId = fromOfficeId; + Long mostRelevantFromClientId = fromClientId; + + Long mostRelevantToOfficeId = toOfficeId; + Long mostRelevantToClientId = toClientId; + + if (fromAccountId != null && fromAccountType == 2) { + fromAccount = this.savingsAccountReadPlatformService.retrieveOne(fromAccountId); + + // override provided fromClient with client of account + mostRelevantFromClientId = fromAccount.clientId(); + } + + if (mostRelevantFromClientId != null) { + fromClient = this.clientReadPlatformService.retrieveOne(mostRelevantFromClientId); + mostRelevantFromOfficeId = fromClient.officeId(); + toClient = fromClient; + fromAccountOptions = this.savingsAccountReadPlatformService.retrieveAllForLookup(mostRelevantFromClientId); + toAccountOptions = new ArrayList(fromAccountOptions); + toAccountOptions.remove(fromAccount); + } + + Collection fromOfficeOptions = null; + Collection fromClientOptions = null; + if (mostRelevantFromOfficeId != null) { + fromOffice = this.officeReadPlatformService.retrieveOffice(mostRelevantFromOfficeId); + fromOfficeOptions = this.officeReadPlatformService.retrieveAllOfficesForDropdown(); + fromClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantFromOfficeId); + } + + // defaults + LocalDate transferDate = DateUtils.getLocalDateOfTenant(); + toOffice = fromOffice; + Collection toOfficeOptions = fromOfficeOptions; + Collection toClientOptions = fromClientOptions; + + if (toAccountId != null) { + toAccount = this.savingsAccountReadPlatformService.retrieveOne(toAccountId); + + // override provided toClient with client of account + mostRelevantToClientId = toAccount.clientId(); + } + + if (mostRelevantToClientId != null && mostRelevantToClientId != mostRelevantFromClientId) { + toClient = this.clientReadPlatformService.retrieveOne(mostRelevantToClientId); + mostRelevantToOfficeId = toClient.officeId(); + + toClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantToOfficeId); + + toAccountOptions = this.savingsAccountReadPlatformService.retrieveAllForLookup(mostRelevantToClientId); + toAccountOptions.remove(fromAccount); + } + + if (mostRelevantToOfficeId != null && mostRelevantToOfficeId != mostRelevantFromOfficeId) { + 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 = this.savingsAccountReadPlatformService.retrieveAllForLookup(mostRelevantToClientId); + toAccountOptions.remove(fromAccount); + } + } + + return AccountTransferData.template(fromOffice, fromClient, fromAccount, transferDate, toOffice, toClient, toAccount, + fromOfficeOptions, fromClientOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountOptions); + } + + @Override + public Page retrieveAll(final SearchParameters searchParameters) { + + StringBuilder sqlBuilder = new StringBuilder(200); + sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append(accountTransfersMapper.schema()); + + 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[] objectArray = new Object[2]; + 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); + } + + @Override + public AccountTransferData retrieveOne(final Long transferId) { + + try { + final String sql = "select " + this.accountTransfersMapper.schema() + " where sat.id = ?"; + + return this.jdbcTemplate.queryForObject(sql, this.accountTransfersMapper, new Object[] { transferId }); + } catch (EmptyResultDataAccessException e) { + throw new AccountTransferNotFoundException(transferId); + } + } + + private static final class AccountTransfersMapper implements RowMapper { + + private final String schemaSql; + + 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("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,"); + 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("tosavacc.id as toSavingsAccountId, tosavacc.account_no as toSavingsAccountNo,"); + sqlBuilder.append("fromsavtran.id as fromSavingsAccountTransactionId,"); + 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_savings_account tosavacc on tosavacc.id = sat.to_savings_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 "); + + this.schemaSql = sqlBuilder.toString(); + } + + public String schema() { + return this.schemaSql; + } + + @Override + public AccountTransferData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = rs.getLong("id"); + final boolean reversed = rs.getBoolean("isReversed"); + + final LocalDate transferDate = JdbcSupport.getLocalDate(rs, "transferDate"); + final BigDecimal transferAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "transferAmount"); + final String transferDescription = rs.getString("transferDescription"); + + 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 CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, + currencyNameCode); + + final Long fromOfficeId = JdbcSupport.getLong(rs, "fromOfficeId"); + final String fromOfficeName = rs.getString("fromOfficeName"); + OfficeData fromOffice = OfficeData.dropdown(fromOfficeId, fromOfficeName, null); + + final Long toOfficeId = JdbcSupport.getLong(rs, "toOfficeId"); + final String toOfficeName = rs.getString("toOfficeName"); + OfficeData toOffice = OfficeData.dropdown(toOfficeId, toOfficeName, null); + + final Long fromClientId = JdbcSupport.getLong(rs, "fromClientId"); + final String fromClientName = rs.getString("fromClientName"); + ClientData fromClient = ClientData.lookup(fromClientId, fromClientName, fromOfficeId, fromOfficeName); + + final Long toClientId = JdbcSupport.getLong(rs, "toClientId"); + final String toClientName = rs.getString("toClientName"); + ClientData toClient = ClientData.lookup(toClientId, toClientName, toOfficeId, toOfficeName); + + final Long fromSavingsAccountId = JdbcSupport.getLong(rs, "fromSavingsAccountId"); + final String fromSavingsAccountNo = rs.getString("fromSavingsAccountNo"); + SavingsAccountData fromSavingsAccount = SavingsAccountData.lookup(fromSavingsAccountId, fromSavingsAccountNo); + + final Long toSavingsAccountId = JdbcSupport.getLong(rs, "toSavingsAccountId"); + final String toSavingsAccountNo = rs.getString("toSavingsAccountNo"); + SavingsAccountData toSavingsAccount = SavingsAccountData.lookup(toSavingsAccountId, toSavingsAccountNo); + + return AccountTransferData.instance(id, reversed, transferDate, currency, transferAmount, transferDescription, fromOffice, + toOffice, fromClient, toClient, fromSavingsAccount, toSavingsAccount); + } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersWritePlatformService.java new file mode 100644 index 000000000..d950863b1 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersWritePlatformService.java @@ -0,0 +1,14 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.accounttransfers.service; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; + +public interface AccountTransfersWritePlatformService { + + CommandProcessingResult create(JsonCommand command); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersWritePlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersWritePlatformServiceImpl.java new file mode 100644 index 000000000..6bc88c0ff --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersWritePlatformServiceImpl.java @@ -0,0 +1,91 @@ +/** + * 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.accounttransfers.service; + +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromAccountIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toAccountIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferAmountParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferDateParamName; + +import java.math.BigDecimal; +import java.util.Locale; + +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; +import org.mifosplatform.portfolio.accounttransfers.data.AccountTransfersDataValidator; +import org.mifosplatform.portfolio.accounttransfers.domain.AccountTransfer; +import org.mifosplatform.portfolio.accounttransfers.domain.AccountTransferAssembler; +import org.mifosplatform.portfolio.accounttransfers.domain.AccountTransferRepository; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; +import org.mifosplatform.portfolio.savings.domain.SavingsAccount; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountDomainService; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class AccountTransfersWritePlatformServiceImpl implements AccountTransfersWritePlatformService { + + private final AccountTransfersDataValidator accountTransfersDataValidator; + private final AccountTransferAssembler accountTransferAssembler; + private final AccountTransferRepository accountTransferRepository; + private final SavingsAccountAssembler savingsAccountAssembler; + private final SavingsAccountDomainService savingsAccountDomainService; + + @Autowired + public AccountTransfersWritePlatformServiceImpl(final AccountTransfersDataValidator accountTransfersDataValidator, + final AccountTransferAssembler accountTransferAssembler, final AccountTransferRepository accountTransferRepository, + final SavingsAccountAssembler savingsAccountAssembler, final SavingsAccountDomainService savingsAccountDomainService) { + this.accountTransfersDataValidator = accountTransfersDataValidator; + this.accountTransferAssembler = accountTransferAssembler; + this.accountTransferRepository = accountTransferRepository; + this.savingsAccountAssembler = savingsAccountAssembler; + this.savingsAccountDomainService = savingsAccountDomainService; + } + + @Transactional + @Override + public CommandProcessingResult create(final JsonCommand command) { + + this.accountTransfersDataValidator.validate(command); + + final LocalDate transactionDate = command.localDateValueOfParameterNamed(transferDateParamName); + final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(transferAmountParamName); + + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); + + // check fromAccountType settings to see if its a savings account + // withdrawal or a loan account transfer + final Long fromSavingsId = command.longValueOfParameterNamed(fromAccountIdParamName); + final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsId); + + final PaymentDetail paymentDetail = null; + + final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, fmt, + transactionDate, transactionAmount, paymentDetail); + + final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName); + final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId); + + final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, fmt, transactionDate, + transactionAmount, paymentDetail); + + AccountTransfer transferTransaction = this.accountTransferAssembler.assembleSavingsToSavingsTransfer(command, withdrawal, deposit); + this.accountTransferRepository.saveAndFlush(transferTransaction); + + return new CommandProcessingResultBuilder() // + .withSavingsId(fromSavingsId) // + .withEntityId(transferTransaction.getId()) // + .build(); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java index 1fc10807d..41f0564e0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java @@ -23,7 +23,6 @@ import org.mifosplatform.portfolio.group.data.GroupGeneralData; */ final public class ClientData implements Comparable { - private final Long id; private final String accountNo; private final String externalId; @@ -49,16 +48,17 @@ final public class ClientData implements Comparable { // associations private final Collection groups; - + // template private final Collection officeOptions; private final Collection staffOptions; @SuppressWarnings("unused") private final Collection closureReasons; - - public static ClientData template(final Long officeId, final LocalDate joinedDate, final Collection officeOptions, + + public static ClientData template(final Long officeId, final LocalDate joinedDate, final Collection officeOptions, final Collection staffOptions, final Collection closureReasons) { - return new ClientData(null, null, officeId, null, null, null, null, null, null, null, null, joinedDate, null, null, null, officeOptions, null, staffOptions, closureReasons); + return new ClientData(null, null, officeId, null, null, null, null, null, null, null, null, joinedDate, null, null, null, + officeOptions, null, staffOptions, closureReasons); } public static ClientData templateOnTop(final ClientData clientData, final ClientData templateData) { @@ -81,11 +81,12 @@ final public class ClientData implements Comparable { final String officeName) { return new ClientData(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName, null, - null, null, null, null, null, null,null, null); + null, null, null, null, null, null, null, null); } public static ClientData lookup(final Long id, final String displayName, final Long officeId, final String officeName) { - return new ClientData(null, null, officeId, officeName, id, null, null, null, null, displayName, null, null, null, null, null, null, null, null, null); + return new ClientData(null, null, officeId, officeName, id, null, null, null, null, displayName, null, null, null, null, null, + null, null, null, null); } public static ClientData instance(final String accountNo, final EnumOptionData status, final Long officeId, final String officeName, @@ -99,8 +100,8 @@ final public class ClientData implements Comparable { private ClientData(final String accountNo, final EnumOptionData status, final Long officeId, final String officeName, final Long id, final String firstname, final String middlename, final String lastname, final String fullname, final String displayName, final String externalId, final LocalDate activationDate, final Long imageId, final Long staffId, final String staffName, - final Collection allowedOffices, final Collection groups, final Collection staffOptions, - final Collection closureReasons) { + final Collection allowedOffices, final Collection groups, + final Collection staffOptions, final Collection closureReasons) { this.accountNo = accountNo; this.status = status; if (status != null) { @@ -122,7 +123,7 @@ final public class ClientData implements Comparable { if (imageId != null) { this.imagePresent = Boolean.TRUE; } else { - this.imagePresent = Boolean.FALSE; + this.imagePresent = null; } this.staffId = staffId; this.staffName = staffName; @@ -189,7 +190,6 @@ final public class ClientData implements Comparable { .toHashCode(); } - // TODO - kw - look into removing usage of the getters below public String getExternalId() { return this.externalId; } @@ -205,5 +205,4 @@ final public class ClientData implements Comparable { public LocalDate getActivationDate() { return this.activationDate; } - } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountData.java index e9be89150..e6da94bd1 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountData.java @@ -8,6 +8,8 @@ package org.mifosplatform.portfolio.savings.data; import java.math.BigDecimal; import java.util.Collection; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; import org.joda.time.LocalDate; import org.joda.time.MonthDay; import org.mifosplatform.infrastructure.core.data.EnumOptionData; @@ -93,6 +95,26 @@ public class SavingsAccountData { interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions); } + public static SavingsAccountData lookup(final Long accountId, final String accountNo) { + + final Collection productOptions = null; + final Collection fieldOfficerOptions = null; + final Collection interestCompoundingPeriodTypeOptions = null; + final Collection interestPostingPeriodTypeOptions = null; + final Collection interestCalculationTypeOptions = null; + final Collection interestCalculationDaysInYearTypeOptions = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + + final Collection transactions = null; + + return new SavingsAccountData(accountId, accountNo, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, transactions, productOptions, + fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, + interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, + withdrawalFeeTypeOptions); + } + public static SavingsAccountData withTemplateOptions(final SavingsAccountData account, final SavingsAccountData template, final Collection transactions) { @@ -263,4 +285,23 @@ public class SavingsAccountData { public Long productId() { return this.savingsProductId; } + + public CurrencyData currency() { + return this.currency; + } + + @Override + public boolean equals(final Object obj) { + + if (obj == null) { return false; } + if (obj == this) { return true; } + if (obj.getClass() != getClass()) { return false; } + SavingsAccountData rhs = (SavingsAccountData) obj; + return new EqualsBuilder().append(this.id, rhs.id).append(this.accountNo, rhs.accountNo).isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37).append(this.id).append(this.accountNo).toHashCode(); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountTransactionData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountTransactionData.java index 28547379e..88d5324b3 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountTransactionData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountTransactionData.java @@ -11,6 +11,7 @@ import java.util.Collection; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.codes.data.CodeValueData; import org.mifosplatform.organisation.monetary.data.CurrencyData; +import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData; import org.mifosplatform.portfolio.paymentdetail.data.PaymentDetailData; /** @@ -29,16 +30,18 @@ public class SavingsAccountTransactionData { private final BigDecimal amount; private final BigDecimal runningBalance; private final boolean reversed; + private final AccountTransferData transfer; // templates final Collection paymentTypeOptions; public static SavingsAccountTransactionData create(final Long id, final SavingsAccountTransactionEnumData transactionType, final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date, - final CurrencyData currency, final BigDecimal amount, final BigDecimal runningBalance, final boolean reversed) { + final CurrencyData currency, final BigDecimal amount, final BigDecimal runningBalance, final boolean reversed, + final AccountTransferData transfer) { final Collection paymentTypeOptions = null; return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency, - amount, runningBalance, reversed, paymentTypeOptions); + amount, runningBalance, reversed, transfer, paymentTypeOptions); } public static SavingsAccountTransactionData template(final Long savingsId, final String savingsAccountNo, @@ -51,7 +54,7 @@ public class SavingsAccountTransactionData { final PaymentDetailData paymentDetailData = null; final Collection paymentTypeOptions = null; return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, defaultLocalDate, - currency, amount, runningBalance, reversed, null); + currency, amount, runningBalance, reversed, null, null); } public static SavingsAccountTransactionData templateOnTop(final SavingsAccountTransactionData savingsAccountTransactionData, @@ -60,13 +63,13 @@ public class SavingsAccountTransactionData { savingsAccountTransactionData.paymentDetailData, savingsAccountTransactionData.accountId, savingsAccountTransactionData.accountNo, savingsAccountTransactionData.date, savingsAccountTransactionData.currency, savingsAccountTransactionData.amount, savingsAccountTransactionData.runningBalance, savingsAccountTransactionData.reversed, - paymentTypeOptions); + savingsAccountTransactionData.transfer, paymentTypeOptions); } private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType, final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date, final CurrencyData currency, final BigDecimal amount, final BigDecimal runningBalance, final boolean reversed, - final Collection paymentTypeOptions) { + final AccountTransferData transfer, final Collection paymentTypeOptions) { this.id = id; this.transactionType = transactionType; this.paymentDetailData = paymentDetailData; @@ -77,6 +80,7 @@ public class SavingsAccountTransactionData { this.amount = amount; this.runningBalance = runningBalance; this.reversed = reversed; + this.transfer = transfer; this.paymentTypeOptions = paymentTypeOptions; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java index 3b4e39e2a..d4dcea6d7 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java @@ -699,7 +699,7 @@ public class SavingsAccount extends AbstractPersistable { boolean transactionBeforeLastInterestPosting = false; for (SavingsAccountTransaction transaction : retreiveListOfTransactions()) { - if (transaction.isNotReversed() && transaction.isInterestPostingAndNotReversed() && transaction.isAfter(transactionDate)) { + if (transaction.isInterestPostingAndNotReversed() && transaction.isAfter(transactionDate)) { transactionBeforeLastInterestPosting = true; break; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainService.java new file mode 100644 index 000000000..96ddfa376 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainService.java @@ -0,0 +1,21 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import java.math.BigDecimal; + +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormatter; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; + +public interface SavingsAccountDomainService { + + SavingsAccountTransaction handleWithdrawal(SavingsAccount account, DateTimeFormatter fmt, LocalDate transactionDate, + BigDecimal transactionAmount, PaymentDetail paymentDetail); + + SavingsAccountTransaction handleDeposit(SavingsAccount account, DateTimeFormatter fmt, LocalDate transactionDate, + BigDecimal transactionAmount, PaymentDetail paymentDetail); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java new file mode 100644 index 000000000..286a28125 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java @@ -0,0 +1,117 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormatter; +import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService; +import org.mifosplatform.infrastructure.core.service.DateUtils; +import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; +import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper; +import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainService { + + private final SavingsAccountRepositoryWrapper savingsAccountRepository; + private final SavingsAccountTransactionRepository savingsAccountTransactionRepository; + private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper; + private final JournalEntryWritePlatformService journalEntryWritePlatformService; + + @Autowired + public SavingsAccountDomainServiceJpa(final SavingsAccountRepositoryWrapper savingsAccountRepository, + final SavingsAccountTransactionRepository savingsAccountTransactionRepository, + final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper, + final JournalEntryWritePlatformService journalEntryWritePlatformService) { + this.savingsAccountRepository = savingsAccountRepository; + this.savingsAccountTransactionRepository = savingsAccountTransactionRepository; + this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper; + this.journalEntryWritePlatformService = journalEntryWritePlatformService; + } + + @Transactional + @Override + public SavingsAccountTransaction handleWithdrawal(final SavingsAccount account, final DateTimeFormatter fmt, + final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail) { + + final List existingTransactionIds = new ArrayList(); + final List existingReversedTransactionIds = new ArrayList(); + + final SavingsAccountTransaction withdrawal = account.withdraw(fmt, transactionDate, transactionAmount, existingTransactionIds, + existingReversedTransactionIds, paymentDetail); + + final MathContext mc = MathContext.DECIMAL64; + if (account.isBeforeLastPostingPeriod(transactionDate)) { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds); + } else { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.calculateInterestUsing(mc, today); + } + + saveTransactionToGenerateTransactionId(withdrawal); + this.savingsAccountRepository.save(account); + + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + + return withdrawal; + } + + @Transactional + @Override + public SavingsAccountTransaction handleDeposit(final SavingsAccount account, final DateTimeFormatter fmt, + final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail) { + + final List existingTransactionIds = new ArrayList(); + final List existingReversedTransactionIds = new ArrayList(); + + final SavingsAccountTransaction deposit = account.deposit(fmt, transactionDate, transactionAmount, existingTransactionIds, + existingReversedTransactionIds, paymentDetail); + + final MathContext mc = MathContext.DECIMAL64; + if (account.isBeforeLastPostingPeriod(transactionDate)) { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds); + } else { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.calculateInterestUsing(mc, today); + } + + saveTransactionToGenerateTransactionId(deposit); + + this.savingsAccountRepository.save(account); + + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + + return deposit; + } + + private Long saveTransactionToGenerateTransactionId(final SavingsAccountTransaction transaction) { + this.savingsAccountTransactionRepository.save(transaction); + return transaction.getId(); + } + + private void postJournalEntries(final SavingsAccount savingsAccount, final List existingTransactionIds, + final List existingReversedTransactionIds) { + + final MonetaryCurrency currency = savingsAccount.getCurrency(); + final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency); + + final Map accountingBridgeData = savingsAccount.deriveAccountingBridgeData(applicationCurrency.toData(), + existingTransactionIds, existingReversedTransactionIds); + journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java index 677d97f4b..684a5a487 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java @@ -17,6 +17,8 @@ public interface SavingsAccountReadPlatformService { Page retrieveAll(SearchParameters searchParameters); + Collection retrieveAllForLookup(Long clientId); + SavingsAccountData retrieveOne(Long savingsId); SavingsAccountData retrieveTemplate(Long clientId, Long groupId, Long productId, boolean staffInSelectedOfficeOnly); 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 d38a8f0d2..75c7a0561 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 @@ -26,6 +26,7 @@ import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.staff.data.StaffData; import org.mifosplatform.organisation.staff.service.StaffReadPlatformService; +import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData; import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; import org.mifosplatform.portfolio.group.data.GroupGeneralData; @@ -74,7 +75,6 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead // pagination private final PaginationHelper paginationHelper = new PaginationHelper(); - @Autowired public SavingsAccountReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource, final ClientReadPlatformService clientReadPlatformService, final GroupReadPlatformService groupReadPlatformService, @@ -93,6 +93,16 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead this.annualFeeMapper = new SavingsAccountAnnualFeeMapper(); } + @Override + public Collection retrieveAllForLookup(final Long clientId) { + + StringBuilder sqlBuilder = new StringBuilder("select " + this.savingAccountMapper.schema()); + sqlBuilder.append(" where sa.client_id = ? and sa.status_enum = 300"); + + Object[] queryParameters = new Object[] { clientId }; + return this.jdbcTemplate.query(sqlBuilder.toString(), this.savingAccountMapper, queryParameters); + } + @Override public Page retrieveAll(final SearchParameters searchParameters) { @@ -148,12 +158,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead public SavingsAccountData retrieveOne(final Long accountId) { try { - this.context.authenticatedUser(); + final String sql = "select " + this.savingAccountMapper.schema() + " where sa.id = ?"; - final SavingAccountMapper mapper = new SavingAccountMapper(); - final String sql = "select " + mapper.schema() + " where sa.id = ?"; - - return this.jdbcTemplate.queryForObject(sql, mapper, new Object[] { accountId }); + return this.jdbcTemplate.queryForObject(sql, this.savingAccountMapper, new Object[] { accountId }); } catch (EmptyResultDataAccessException e) { throw new SavingsAccountNotFoundException(accountId); } @@ -518,6 +525,12 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead sqlBuilder.append("tr.id as transactionId, tr.transaction_type_enum as transactionType, "); sqlBuilder.append("tr.transaction_date as transactionDate, tr.amount as transactionAmount,"); sqlBuilder.append("tr.running_balance_derived as runningBalance, tr.is_reversed as reversed,"); + sqlBuilder.append("fromtran.id as fromTransferId, fromtran.is_reversed as fromTransferReversed,"); + sqlBuilder.append("fromtran.transaction_date as fromTransferDate, fromtran.amount as fromTransferAmount,"); + sqlBuilder.append("fromtran.description as fromTransferDescription,"); + sqlBuilder.append("totran.id as toTransferId, totran.is_reversed as toTransferReversed,"); + sqlBuilder.append("totran.transaction_date as toTransferDate, totran.amount as toTransferAmount,"); + sqlBuilder.append("totran.description as toTransferDescription,"); sqlBuilder.append("sa.id as savingsId, sa.account_no as accountNo,"); sqlBuilder.append("pd.payment_type_cv_id as paymentType,pd.account_number as accountNumber,pd.check_number as checkNumber, "); sqlBuilder.append("pd.receipt_number as receiptNumber, pd.bank_number as bankNumber,pd.routing_code as routingCode, "); @@ -528,6 +541,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_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 "); @@ -576,8 +591,29 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, currencyNameCode); + AccountTransferData transfer = null; + final Long fromTransferId = JdbcSupport.getLong(rs, "fromTransferId"); + final Long toTransferId = JdbcSupport.getLong(rs, "toTransferId"); + if (fromTransferId != null) { + final LocalDate fromTransferDate = JdbcSupport.getLocalDate(rs, "fromTransferDate"); + final BigDecimal fromTransferAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "fromTransferAmount"); + final boolean fromTransferReversed = rs.getBoolean("fromTransferReversed"); + final String fromTransferDescription = rs.getString("fromTransferDescription"); + + transfer = AccountTransferData.transferBasicDetails(fromTransferId, currency, fromTransferAmount, fromTransferDate, + fromTransferDescription, fromTransferReversed); + } else if (toTransferId != null) { + final LocalDate toTransferDate = JdbcSupport.getLocalDate(rs, "toTransferDate"); + final BigDecimal toTransferAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "toTransferAmount"); + final boolean toTransferReversed = rs.getBoolean("toTransferReversed"); + final String toTransferDescription = rs.getString("toTransferDescription"); + + transfer = AccountTransferData.transferBasicDetails(toTransferId, currency, toTransferAmount, toTransferDate, + toTransferDescription, toTransferReversed); + } + return SavingsAccountTransactionData.create(id, transactionType, paymentDetailData, savingsId, accountNo, date, currency, - amount, runningBalance, reversed); + amount, runningBalance, reversed, transfer); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java index d53241b28..119915d13 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java @@ -33,6 +33,7 @@ import org.mifosplatform.portfolio.paymentdetail.service.PaymentDetailWritePlatf import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionDataValidator; import org.mifosplatform.portfolio.savings.domain.SavingsAccount; import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountDomainService; import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepository; import org.mifosplatform.portfolio.savings.domain.SavingsAccountStatusType; import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction; @@ -53,6 +54,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi private final PaymentDetailWritePlatformService paymentDetailWritePlatformService; private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper; private final JournalEntryWritePlatformService journalEntryWritePlatformService; + private final SavingsAccountDomainService savingsAccountDomainService; @Autowired public SavingsAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, @@ -62,7 +64,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi final SavingsAccountTransactionDataValidator savingsAccountTransactionDataValidator, final PaymentDetailWritePlatformService paymentDetailWritePlatformService, final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper, - final JournalEntryWritePlatformService journalEntryWritePlatformService) { + final JournalEntryWritePlatformService journalEntryWritePlatformService, + final SavingsAccountDomainService savingsAccountDomainService) { this.context = context; this.savingAccountRepository = savingAccountRepository; this.savingsAccountTransactionRepository = savingsAccountTransactionRepository; @@ -71,6 +74,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi this.paymentDetailWritePlatformService = paymentDetailWritePlatformService; this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper; this.journalEntryWritePlatformService = journalEntryWritePlatformService; + this.savingsAccountDomainService = savingsAccountDomainService; } @Transactional @@ -120,36 +124,19 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); - final List existingTransactionIds = new ArrayList(); - final List existingReversedTransactionIds = new ArrayList(); final Map changes = new LinkedHashMap(); PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); - final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN); - final SavingsAccountTransaction deposit = account.deposit(fmt, transactionDate, transactionAmount, existingTransactionIds, - existingReversedTransactionIds, paymentDetail); - - if (account.isBeforeLastPostingPeriod(transactionDate)) { - final LocalDate today = DateUtils.getLocalDateOfTenant(); - account.postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds); - } else { - final LocalDate today = DateUtils.getLocalDateOfTenant(); - account.calculateInterestUsing(mc, today); - } - - final Long transactionId = saveTransactionToGenerateTransactionId(deposit); - - this.savingAccountRepository.save(account); - - postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(account, fmt, transactionDate, + transactionAmount, paymentDetail); return new CommandProcessingResultBuilder() // - .withEntityId(transactionId) // + .withEntityId(deposit.getId()) // .withOfficeId(account.officeId()) // .withClientId(account.clientId()) // .withGroupId(account.groupId()) // .withSavingsId(savingsId) // - .with(changes)// + .with(changes) // .build(); } @@ -162,42 +149,24 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi @Override public CommandProcessingResult withdrawal(final Long savingsId, final JsonCommand command) { - this.context.authenticatedUser(); - this.savingsAccountTransactionDataValidator.validate(command); - final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId); - final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); final Locale locale = command.extractLocale(); final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); - final List existingTransactionIds = new ArrayList(); - final List existingReversedTransactionIds = new ArrayList(); final Map changes = new LinkedHashMap(); PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); - final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN); - final SavingsAccountTransaction withdrawal = account.withdraw(fmt, transactionDate, transactionAmount, existingTransactionIds, - existingReversedTransactionIds, paymentDetail); + final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId); - if (account.isBeforeLastPostingPeriod(transactionDate)) { - final LocalDate today = DateUtils.getLocalDateOfTenant(); - account.postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds); - } else { - final LocalDate today = DateUtils.getLocalDateOfTenant(); - account.calculateInterestUsing(mc, today); - } - - final Long transactionId = saveTransactionToGenerateTransactionId(withdrawal); - this.savingAccountRepository.save(account); - - postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(account, fmt, transactionDate, + transactionAmount, paymentDetail); return new CommandProcessingResultBuilder() // - .withEntityId(transactionId) // + .withEntityId(withdrawal.getId()) // .withOfficeId(account.officeId()) // .withClientId(account.clientId()) // .withGroupId(account.groupId()) // @@ -273,7 +242,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi .build(); } - @Transactional private void postInterest(final SavingsAccount account) { final List existingTransactionIds = new ArrayList(); @@ -290,7 +258,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi @CronTarget(jobName = JobName.POST_INTEREST_FOR_SAVINGS) @Override public void postInterestForAccounts() { - final List savingsAccounts = this.savingAccountRepository.findSavingAccountByStatus(SavingsAccountStatusType.ACTIVE.getValue()); + final List savingsAccounts = this.savingAccountRepository.findSavingAccountByStatus(SavingsAccountStatusType.ACTIVE + .getValue()); for (final SavingsAccount savingsAccount : savingsAccounts) { this.savingAccountAssembler.assignSavingAccountHelpers(savingsAccount); postInterest(savingsAccount);