From 83cf141f57e4421ab49832fe88e6398cb0832c58 Mon Sep 17 00:00:00 2001 From: Udai Gupta Date: Sat, 8 Jun 2013 23:37:11 +0530 Subject: [PATCH 01/16] fix file name : fails on *nix due to case sensitivity --- mifosng-provider/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mifosng-provider/build.gradle b/mifosng-provider/build.gradle index 10cf889f3..04bb6b2ba 100644 --- a/mifosng-provider/build.gradle +++ b/mifosng-provider/build.gradle @@ -226,7 +226,7 @@ war { } license { - header rootProject.file('../LICENSE.MD') + header rootProject.file('../LICENSE.md') strictCheck true } @@ -407,4 +407,4 @@ def qualifyVersionIfNecessary(version) { } } return version -} \ No newline at end of file +} From 6a8eb79e97cd658ebec2320180e1b7a15dcf2815 Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Mon, 10 Jun 2013 15:37:55 +0200 Subject: [PATCH 02/16] update for 1.3.0 release --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 45499b9bc..ae37f6448 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ The mifos-platform (project named 'Mifos X') has reached public release versi That means there is a published and stable API. This API is documented in the api-docs under Full API Matrix. -Latest stable release: mifosplatform-1.2.1 +Latest stable release: mifosplatform-1.3.0 License ============= From 30fd2faff69dc501d1e2738164ddf45db48e69dc Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Mon, 10 Jun 2013 15:50:56 +0200 Subject: [PATCH 03/16] update for 1.3.0 release --- CHANGELOG.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fee6ccf3e..da8c61055 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ See VERSIONING (https://github.com/openMF/mifosx/blob/master/VERSIONING.md) for Releases =============== +1. 10 Jun 2013 - 1.3.0.RELEASE 1. 06 Jun 2013 - 1.2.1.RELEASE 1. 27 May 2013 - 1.2.0.RELEASE 1. 24 May 2013 - 1.1.4.RELEASE @@ -23,6 +24,42 @@ Releases 1. 05 April 2013 - 0.11.1.beta 1. 05 April 2013 - 0.11.0.beta +1.3.0.RELEASE +============= +Feature Release + +This feature release focussed mainly on features requested by Musoni for their upcoming m-lite product and by Quipu technolgies for their TEVI project. + +Key changes are improvements to accounting and flexible support for grace concepts in loan repayment schedule. + +Platform & API + +Bug + - [MIFOSX-427] - Charges and penalties functionality wonky when multiple charges/penalties are due on the same date + +New Features & Improvements + - [MIFOSX-391] - "Advanced" accounting rules for compound Journal entries + - [MIFOSX-413] - Ability to account different charges (both fees and penalties) under different heads + - [MIFOSX-295] - Support Prinicpal and Interest Grace Periods for Loans, Loan Schedules + - [MIFOSX-297] - Support over payment of loan scenarios common when repayments through mobile, third party + - [MIFOSX-374] - TEVI: Repayment strategy to support payment ordering of Interest, Principal, Penalties, Fees + - [MIFOSX-376] - TEVI: Track additional details on the loan repayment schedule + +Reference App + - [MIFOSX-405] - In client's General page not able to differentiate between Activated and not Activated savings accounts. + - [MIFOSX-416] - Distinguish between Individual and JLG loans in clients general details + - [MIFOSX-418] - Hide/show Centers and center creation based on permissions + - [MIFOSX-419] - Display Center summary details in UI + - [MIFOSX-423] - Unable to view group loan in group context + +BETA (functionality related to BETA functionality + - [MIFOSX-344] - Add overnight scheduled task to detect if annual fee is due on savings account and apply it + - [MIFOSX-325] - GSOC 2013 - Savings accounts server side sorting and pagination on retrieve all + - [MIFOSX-310] - Generate Collection sheet based on meeting calendar dates + - [MIFOSX-356] - Modify unassign staff API call in group + - [MIFOSX-69] - Amazon S3 appender for document upload + - [MIFOSX-395] - TEVI: Support hierarchial staff and organisational roles on staff with linkage to Application User + 1.2.1.RELEASE ============= Bug Release From 5e3a10bf2b2e0ebfed7a642c376cdf9da02517a2 Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Thu, 20 Jun 2013 11:20:30 +0200 Subject: [PATCH 04/16] update for 1.4.0 release --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ae37f6448..e46a6b860 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ The mifos-platform (project named 'Mifos X') has reached public release versi That means there is a published and stable API. This API is documented in the api-docs under Full API Matrix. -Latest stable release: mifosplatform-1.3.0 +Latest stable release: mifosplatform-1.4.0 License ============= From dd29b41aab20a1f4403a563b8098d74b6398823a Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Thu, 20 Jun 2013 10:39:55 +0100 Subject: [PATCH 05/16] Revert "update for 1.4.0 release" This reverts commit 5e3a10bf2b2e0ebfed7a642c376cdf9da02517a2. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e46a6b860..ae37f6448 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ The mifos-platform (project named 'Mifos X') has reached public release versi That means there is a published and stable API. This API is documented in the api-docs under Full API Matrix. -Latest stable release: mifosplatform-1.4.0 +Latest stable release: mifosplatform-1.3.0 License ============= From 14589590a6a95d3aca9e76c0cc1a9e241c9aee1f Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Thu, 20 Jun 2013 12:04:53 +0100 Subject: [PATCH 06/16] update release for 1.4.1 candidate. --- api-docs/apiLive.htm | 2 +- mifosng-provider/gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 064e2997d..f8629176d 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -90,7 +90,7 @@
- +

Overview

diff --git a/mifosng-provider/gradle.properties b/mifosng-provider/gradle.properties index bab03384b..63c3accc6 100644 --- a/mifosng-provider/gradle.properties +++ b/mifosng-provider/gradle.properties @@ -1 +1 @@ -releaseVersion=1.4.0.RELEASE \ No newline at end of file +releaseVersion=1.4.1.DEVELOP \ No newline at end of file From 97f681244a0ffbe6f4be2e0c3fd5f8ef769e1913 Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Thu, 20 Jun 2013 12:53:58 +0100 Subject: [PATCH 07/16] MIFOSX-479: run scheduled jobs for all tenants. --- .../ScheduledJobRunnerServiceImpl.java | 229 +++++++++--------- 1 file changed, 117 insertions(+), 112 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java index fd517a8d5..638f2b295 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java @@ -40,144 +40,149 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService @Override public void updateLoanSummaryDetails() { - final MifosPlatformTenant tenant = this.tenantDetailsService.loadTenantById("default"); + List allTenants = this.tenantDetailsService.findAllTenants(); + for (MifosPlatformTenant tenant : allTenants) { + ThreadLocalContextUtil.setTenant(tenant); - ThreadLocalContextUtil.setTenant(tenant); + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourcePerTenantService.retrieveTenantAwareDataSource()); - JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourcePerTenantService.retrieveTenantAwareDataSource()); + StringBuilder updateSqlBuilder = new StringBuilder(900); + updateSqlBuilder.append("update m_loan "); + updateSqlBuilder.append("join ("); + updateSqlBuilder.append("SELECT ml.id AS loanId,"); + updateSqlBuilder.append("SUM(mr.principal_amount) as principal_disbursed_derived, "); + updateSqlBuilder.append("SUM(IFNULL(mr.principal_completed_derived,0)) as principal_repaid_derived, "); + updateSqlBuilder.append("SUM(IFNULL(mr.principal_writtenoff_derived,0)) as principal_writtenoff_derived,"); + updateSqlBuilder.append("SUM(IFNULL(mr.interest_amount,0)) as interest_charged_derived,"); + updateSqlBuilder.append("SUM(IFNULL(mr.interest_completed_derived,0)) as interest_repaid_derived,"); + updateSqlBuilder.append("SUM(IFNULL(mr.interest_waived_derived,0)) as interest_waived_derived,"); + updateSqlBuilder.append("SUM(IFNULL(mr.interest_writtenoff_derived,0)) as interest_writtenoff_derived,"); + updateSqlBuilder.append("SUM(IFNULL(mr.fee_charges_amount,0)) as fee_charges_charged_derived,"); + updateSqlBuilder.append("SUM(IFNULL(mr.fee_charges_completed_derived,0)) as fee_charges_repaid_derived,"); + updateSqlBuilder.append("SUM(IFNULL(mr.fee_charges_waived_derived,0)) as fee_charges_waived_derived,"); + updateSqlBuilder.append("SUM(IFNULL(mr.fee_charges_writtenoff_derived,0)) as fee_charges_writtenoff_derived,"); + updateSqlBuilder.append("SUM(IFNULL(mr.penalty_charges_amount,0)) as penalty_charges_charged_derived,"); + updateSqlBuilder.append("SUM(IFNULL(mr.penalty_charges_completed_derived,0)) as penalty_charges_repaid_derived,"); + updateSqlBuilder.append("SUM(IFNULL(mr.penalty_charges_waived_derived,0)) as penalty_charges_waived_derived,"); + updateSqlBuilder.append("SUM(IFNULL(mr.penalty_charges_writtenoff_derived,0)) as penalty_charges_writtenoff_derived "); + updateSqlBuilder.append(" FROM m_loan ml "); + updateSqlBuilder.append("INNER JOIN m_loan_repayment_schedule mr on mr.loan_id = ml.id "); + updateSqlBuilder.append("WHERE ml.disbursedon_date is not null "); + updateSqlBuilder.append("GROUP BY ml.id "); + updateSqlBuilder.append(") x on x.loanId = m_loan.id "); - StringBuilder updateSqlBuilder = new StringBuilder(900); - updateSqlBuilder.append("update m_loan "); - updateSqlBuilder.append("join ("); - updateSqlBuilder.append("SELECT ml.id AS loanId,"); - updateSqlBuilder.append("SUM(mr.principal_amount) as principal_disbursed_derived, "); - updateSqlBuilder.append("SUM(IFNULL(mr.principal_completed_derived,0)) as principal_repaid_derived, "); - updateSqlBuilder.append("SUM(IFNULL(mr.principal_writtenoff_derived,0)) as principal_writtenoff_derived,"); - updateSqlBuilder.append("SUM(IFNULL(mr.interest_amount,0)) as interest_charged_derived,"); - updateSqlBuilder.append("SUM(IFNULL(mr.interest_completed_derived,0)) as interest_repaid_derived,"); - updateSqlBuilder.append("SUM(IFNULL(mr.interest_waived_derived,0)) as interest_waived_derived,"); - updateSqlBuilder.append("SUM(IFNULL(mr.interest_writtenoff_derived,0)) as interest_writtenoff_derived,"); - updateSqlBuilder.append("SUM(IFNULL(mr.fee_charges_amount,0)) as fee_charges_charged_derived,"); - updateSqlBuilder.append("SUM(IFNULL(mr.fee_charges_completed_derived,0)) as fee_charges_repaid_derived,"); - updateSqlBuilder.append("SUM(IFNULL(mr.fee_charges_waived_derived,0)) as fee_charges_waived_derived,"); - updateSqlBuilder.append("SUM(IFNULL(mr.fee_charges_writtenoff_derived,0)) as fee_charges_writtenoff_derived,"); - updateSqlBuilder.append("SUM(IFNULL(mr.penalty_charges_amount,0)) as penalty_charges_charged_derived,"); - updateSqlBuilder.append("SUM(IFNULL(mr.penalty_charges_completed_derived,0)) as penalty_charges_repaid_derived,"); - updateSqlBuilder.append("SUM(IFNULL(mr.penalty_charges_waived_derived,0)) as penalty_charges_waived_derived,"); - updateSqlBuilder.append("SUM(IFNULL(mr.penalty_charges_writtenoff_derived,0)) as penalty_charges_writtenoff_derived "); - updateSqlBuilder.append(" FROM m_loan ml "); - updateSqlBuilder.append("INNER JOIN m_loan_repayment_schedule mr on mr.loan_id = ml.id "); - updateSqlBuilder.append("WHERE ml.disbursedon_date is not null "); - updateSqlBuilder.append("GROUP BY ml.id "); - updateSqlBuilder.append(") x on x.loanId = m_loan.id "); + updateSqlBuilder.append("SET m_loan.principal_disbursed_derived = x.principal_disbursed_derived,"); + updateSqlBuilder.append("m_loan.principal_repaid_derived = x.principal_repaid_derived,"); + updateSqlBuilder.append("m_loan.principal_writtenoff_derived = x.principal_writtenoff_derived,"); + updateSqlBuilder + .append("m_loan.principal_outstanding_derived = (x.principal_disbursed_derived - (x.principal_repaid_derived + x.principal_writtenoff_derived)),"); + updateSqlBuilder.append("m_loan.interest_charged_derived = x.interest_charged_derived,"); + updateSqlBuilder.append("m_loan.interest_repaid_derived = x.interest_repaid_derived,"); + updateSqlBuilder.append("m_loan.interest_waived_derived = x.interest_waived_derived,"); + updateSqlBuilder.append("m_loan.interest_writtenoff_derived = x.interest_writtenoff_derived,"); + updateSqlBuilder + .append("m_loan.interest_outstanding_derived = (x.interest_charged_derived - (x.interest_repaid_derived + x.interest_waived_derived + x.interest_writtenoff_derived)),"); + updateSqlBuilder.append("m_loan.fee_charges_charged_derived = x.fee_charges_charged_derived,"); + updateSqlBuilder.append("m_loan.fee_charges_repaid_derived = x.fee_charges_repaid_derived,"); + updateSqlBuilder.append("m_loan.fee_charges_waived_derived = x.fee_charges_waived_derived,"); + updateSqlBuilder.append("m_loan.fee_charges_writtenoff_derived = x.fee_charges_writtenoff_derived,"); + updateSqlBuilder + .append("m_loan.fee_charges_outstanding_derived = (x.fee_charges_charged_derived - (x.fee_charges_repaid_derived + x.fee_charges_waived_derived + x.fee_charges_writtenoff_derived)),"); + updateSqlBuilder.append("m_loan.penalty_charges_charged_derived = x.penalty_charges_charged_derived,"); + updateSqlBuilder.append("m_loan.penalty_charges_repaid_derived = x.penalty_charges_repaid_derived,"); + updateSqlBuilder.append("m_loan.penalty_charges_waived_derived = x.penalty_charges_waived_derived,"); + updateSqlBuilder.append("m_loan.penalty_charges_writtenoff_derived = x.penalty_charges_writtenoff_derived,"); + updateSqlBuilder + .append("m_loan.penalty_charges_outstanding_derived = (x.penalty_charges_charged_derived - (x.penalty_charges_repaid_derived + x.penalty_charges_waived_derived + x.penalty_charges_writtenoff_derived)),"); + updateSqlBuilder + .append("m_loan.total_expected_repayment_derived = (x.principal_disbursed_derived + x.interest_charged_derived + x.fee_charges_charged_derived + x.penalty_charges_charged_derived),"); + updateSqlBuilder + .append("m_loan.total_repayment_derived = (x.principal_repaid_derived + x.interest_repaid_derived + x.fee_charges_repaid_derived + x.penalty_charges_repaid_derived),"); + updateSqlBuilder + .append("m_loan.total_expected_costofloan_derived = (x.interest_charged_derived + x.fee_charges_charged_derived + x.penalty_charges_charged_derived),"); + updateSqlBuilder + .append("m_loan.total_costofloan_derived = (x.interest_repaid_derived + x.fee_charges_repaid_derived + x.penalty_charges_repaid_derived),"); + updateSqlBuilder + .append("m_loan.total_waived_derived = (x.interest_waived_derived + x.fee_charges_waived_derived + x.penalty_charges_waived_derived),"); + updateSqlBuilder + .append("m_loan.total_writtenoff_derived = (x.interest_writtenoff_derived + x.fee_charges_writtenoff_derived + x.penalty_charges_writtenoff_derived),"); + updateSqlBuilder.append("m_loan.total_outstanding_derived="); + updateSqlBuilder.append(" (x.principal_disbursed_derived - (x.principal_repaid_derived + x.principal_writtenoff_derived)) + "); + updateSqlBuilder + .append(" (x.interest_charged_derived - (x.interest_repaid_derived + x.interest_waived_derived + x.interest_writtenoff_derived)) +"); + updateSqlBuilder + .append(" (x.fee_charges_charged_derived - (x.fee_charges_repaid_derived + x.fee_charges_waived_derived + x.fee_charges_writtenoff_derived)) +"); + updateSqlBuilder + .append(" (x.penalty_charges_charged_derived - (x.penalty_charges_repaid_derived + x.penalty_charges_waived_derived + x.penalty_charges_writtenoff_derived))"); - updateSqlBuilder.append("SET m_loan.principal_disbursed_derived = x.principal_disbursed_derived,"); - updateSqlBuilder.append("m_loan.principal_repaid_derived = x.principal_repaid_derived,"); - updateSqlBuilder.append("m_loan.principal_writtenoff_derived = x.principal_writtenoff_derived,"); - updateSqlBuilder - .append("m_loan.principal_outstanding_derived = (x.principal_disbursed_derived - (x.principal_repaid_derived + x.principal_writtenoff_derived)),"); - updateSqlBuilder.append("m_loan.interest_charged_derived = x.interest_charged_derived,"); - updateSqlBuilder.append("m_loan.interest_repaid_derived = x.interest_repaid_derived,"); - updateSqlBuilder.append("m_loan.interest_waived_derived = x.interest_waived_derived,"); - updateSqlBuilder.append("m_loan.interest_writtenoff_derived = x.interest_writtenoff_derived,"); - updateSqlBuilder - .append("m_loan.interest_outstanding_derived = (x.interest_charged_derived - (x.interest_repaid_derived + x.interest_waived_derived + x.interest_writtenoff_derived)),"); - updateSqlBuilder.append("m_loan.fee_charges_charged_derived = x.fee_charges_charged_derived,"); - updateSqlBuilder.append("m_loan.fee_charges_repaid_derived = x.fee_charges_repaid_derived,"); - updateSqlBuilder.append("m_loan.fee_charges_waived_derived = x.fee_charges_waived_derived,"); - updateSqlBuilder.append("m_loan.fee_charges_writtenoff_derived = x.fee_charges_writtenoff_derived,"); - updateSqlBuilder - .append("m_loan.fee_charges_outstanding_derived = (x.fee_charges_charged_derived - (x.fee_charges_repaid_derived + x.fee_charges_waived_derived + x.fee_charges_writtenoff_derived)),"); - updateSqlBuilder.append("m_loan.penalty_charges_charged_derived = x.penalty_charges_charged_derived,"); - updateSqlBuilder.append("m_loan.penalty_charges_repaid_derived = x.penalty_charges_repaid_derived,"); - updateSqlBuilder.append("m_loan.penalty_charges_waived_derived = x.penalty_charges_waived_derived,"); - updateSqlBuilder.append("m_loan.penalty_charges_writtenoff_derived = x.penalty_charges_writtenoff_derived,"); - updateSqlBuilder - .append("m_loan.penalty_charges_outstanding_derived = (x.penalty_charges_charged_derived - (x.penalty_charges_repaid_derived + x.penalty_charges_waived_derived + x.penalty_charges_writtenoff_derived)),"); - updateSqlBuilder - .append("m_loan.total_expected_repayment_derived = (x.principal_disbursed_derived + x.interest_charged_derived + x.fee_charges_charged_derived + x.penalty_charges_charged_derived),"); - updateSqlBuilder - .append("m_loan.total_repayment_derived = (x.principal_repaid_derived + x.interest_repaid_derived + x.fee_charges_repaid_derived + x.penalty_charges_repaid_derived),"); - updateSqlBuilder - .append("m_loan.total_expected_costofloan_derived = (x.interest_charged_derived + x.fee_charges_charged_derived + x.penalty_charges_charged_derived),"); - updateSqlBuilder - .append("m_loan.total_costofloan_derived = (x.interest_repaid_derived + x.fee_charges_repaid_derived + x.penalty_charges_repaid_derived),"); - updateSqlBuilder - .append("m_loan.total_waived_derived = (x.interest_waived_derived + x.fee_charges_waived_derived + x.penalty_charges_waived_derived),"); - updateSqlBuilder - .append("m_loan.total_writtenoff_derived = (x.interest_writtenoff_derived + x.fee_charges_writtenoff_derived + x.penalty_charges_writtenoff_derived),"); - updateSqlBuilder.append("m_loan.total_outstanding_derived="); - updateSqlBuilder.append(" (x.principal_disbursed_derived - (x.principal_repaid_derived + x.principal_writtenoff_derived)) + "); - updateSqlBuilder - .append(" (x.interest_charged_derived - (x.interest_repaid_derived + x.interest_waived_derived + x.interest_writtenoff_derived)) +"); - updateSqlBuilder - .append(" (x.fee_charges_charged_derived - (x.fee_charges_repaid_derived + x.fee_charges_waived_derived + x.fee_charges_writtenoff_derived)) +"); - updateSqlBuilder - .append(" (x.penalty_charges_charged_derived - (x.penalty_charges_repaid_derived + x.penalty_charges_waived_derived + x.penalty_charges_writtenoff_derived))"); + int result = jdbcTemplate.update(updateSqlBuilder.toString()); - int result = jdbcTemplate.update(updateSqlBuilder.toString()); - - logger.info("Results affected by update: " + result); + logger.info(tenant.getName() + ": Results affected by update: " + result); + } } @Transactional @Override public void updateLoanArrearsAgeingDetails() { - final MifosPlatformTenant tenant = this.tenantDetailsService.loadTenantById("default"); - ThreadLocalContextUtil.setTenant(tenant); + List allTenants = this.tenantDetailsService.findAllTenants(); + for (MifosPlatformTenant tenant : allTenants) { + ThreadLocalContextUtil.setTenant(tenant); - JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourcePerTenantService.retrieveTenantAwareDataSource()); + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourcePerTenantService.retrieveTenantAwareDataSource()); - jdbcTemplate.execute("truncate table m_loan_arrears_aging"); + jdbcTemplate.execute("truncate table m_loan_arrears_aging"); - StringBuilder updateSqlBuilder = new StringBuilder(900); + StringBuilder updateSqlBuilder = new StringBuilder(900); - updateSqlBuilder - .append("INSERT INTO m_loan_arrears_aging(`loan_id`,`principal_overdue_derived`,`interest_overdue_derived`,`fee_charges_overdue_derived`,`penalty_charges_overdue_derived`,`total_overdue_derived`,`overdue_since_date_derived`)"); - updateSqlBuilder.append("select ml.id as loanId,"); - updateSqlBuilder - .append("SUM((ifnull(mr.principal_amount,0) - ifnull(mr.principal_completed_derived, 0))) as principal_overdue_derived,"); - updateSqlBuilder - .append("SUM((ifnull(mr.interest_amount,0) - ifnull(mr.interest_completed_derived, 0))) as interest_overdue_derived,"); - updateSqlBuilder - .append("SUM((ifnull(mr.fee_charges_amount,0) - ifnull(mr.fee_charges_completed_derived, 0))) as fee_charges_overdue_derived,"); - updateSqlBuilder - .append("SUM((ifnull(mr.penalty_charges_amount,0) - ifnull(mr.penalty_charges_completed_derived, 0))) as penalty_charges_overdue_derived,"); - updateSqlBuilder.append("SUM((ifnull(mr.principal_amount,0) - ifnull(mr.principal_completed_derived, 0))) +"); - updateSqlBuilder.append("SUM((ifnull(mr.interest_amount,0) - ifnull(mr.interest_completed_derived, 0))) +"); - updateSqlBuilder.append("SUM((ifnull(mr.fee_charges_amount,0) - ifnull(mr.fee_charges_completed_derived, 0))) +"); - updateSqlBuilder - .append("SUM((ifnull(mr.penalty_charges_amount,0) - ifnull(mr.penalty_charges_completed_derived, 0))) as total_overdue_derived,"); - updateSqlBuilder.append("MIN(mr.duedate) as overdue_since_date_derived "); - updateSqlBuilder.append(" FROM m_loan ml "); - updateSqlBuilder.append(" INNER JOIN m_loan_repayment_schedule mr on mr.loan_id = ml.id "); - updateSqlBuilder.append(" WHERE ml.loan_status_id = 300 "); // active - updateSqlBuilder.append(" and mr.completed_derived is false "); - updateSqlBuilder.append(" and mr.duedate < CURDATE() "); - updateSqlBuilder.append(" GROUP BY ml.id"); + updateSqlBuilder + .append("INSERT INTO m_loan_arrears_aging(`loan_id`,`principal_overdue_derived`,`interest_overdue_derived`,`fee_charges_overdue_derived`,`penalty_charges_overdue_derived`,`total_overdue_derived`,`overdue_since_date_derived`)"); + updateSqlBuilder.append("select ml.id as loanId,"); + updateSqlBuilder + .append("SUM((ifnull(mr.principal_amount,0) - ifnull(mr.principal_completed_derived, 0))) as principal_overdue_derived,"); + updateSqlBuilder + .append("SUM((ifnull(mr.interest_amount,0) - ifnull(mr.interest_completed_derived, 0))) as interest_overdue_derived,"); + updateSqlBuilder + .append("SUM((ifnull(mr.fee_charges_amount,0) - ifnull(mr.fee_charges_completed_derived, 0))) as fee_charges_overdue_derived,"); + updateSqlBuilder + .append("SUM((ifnull(mr.penalty_charges_amount,0) - ifnull(mr.penalty_charges_completed_derived, 0))) as penalty_charges_overdue_derived,"); + updateSqlBuilder.append("SUM((ifnull(mr.principal_amount,0) - ifnull(mr.principal_completed_derived, 0))) +"); + updateSqlBuilder.append("SUM((ifnull(mr.interest_amount,0) - ifnull(mr.interest_completed_derived, 0))) +"); + updateSqlBuilder.append("SUM((ifnull(mr.fee_charges_amount,0) - ifnull(mr.fee_charges_completed_derived, 0))) +"); + updateSqlBuilder + .append("SUM((ifnull(mr.penalty_charges_amount,0) - ifnull(mr.penalty_charges_completed_derived, 0))) as total_overdue_derived,"); + updateSqlBuilder.append("MIN(mr.duedate) as overdue_since_date_derived "); + updateSqlBuilder.append(" FROM m_loan ml "); + updateSqlBuilder.append(" INNER JOIN m_loan_repayment_schedule mr on mr.loan_id = ml.id "); + updateSqlBuilder.append(" WHERE ml.loan_status_id = 300 "); // active + updateSqlBuilder.append(" and mr.completed_derived is false "); + updateSqlBuilder.append(" and mr.duedate < CURDATE() "); + updateSqlBuilder.append(" GROUP BY ml.id"); - int result = jdbcTemplate.update(updateSqlBuilder.toString()); + int result = jdbcTemplate.update(updateSqlBuilder.toString()); - logger.info("Results affected by update: " + result); + logger.info(tenant.getName() + ": Results affected by update: " + result); + } } @Transactional @Override public void applyAnnualFeeForSavings() { - final MifosPlatformTenant tenant = this.tenantDetailsService.loadTenantById("default"); - ThreadLocalContextUtil.setTenant(tenant); + List allTenants = this.tenantDetailsService.findAllTenants(); + for (MifosPlatformTenant tenant : allTenants) { + ThreadLocalContextUtil.setTenant(tenant); - JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourcePerTenantService.retrieveTenantAwareDataSource()); + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourcePerTenantService.retrieveTenantAwareDataSource()); - String sql = "SELECT ms.id as savingsAccountId FROM m_savings_account ms WHERE ms.annual_fee_next_due_date <= NOW() "; - List savingsIds = jdbcTemplate.queryForList(sql, Long.class); + String sql = "SELECT ms.id as savingsAccountId FROM m_savings_account ms WHERE ms.annual_fee_next_due_date <= NOW() "; + List savingsIds = jdbcTemplate.queryForList(sql, Long.class); - for (Long savingsId : savingsIds) { - this.savingsAccountWritePlatformService.applyAnnualFee(savingsId); + for (Long savingsId : savingsIds) { + this.savingsAccountWritePlatformService.applyAnnualFee(savingsId); + } + + logger.info(tenant.getName() + ": Results affected by update: " + savingsIds.size()); } - - logger.info("Results affected by update: " + savingsIds.size()); } } \ No newline at end of file From c008ed7ff1b32e01dab33a6710407de1fe9090b1 Mon Sep 17 00:00:00 2001 From: mifoscontributer Date: Sat, 22 Jun 2013 13:49:08 +0530 Subject: [PATCH 08/16] Use Login user hierarchy for view accounting rules --- .../rule/api/AccountingRuleApiResource.java | 13 +++++++++---- .../service/AccountingRuleReadPlatformService.java | 2 +- .../AccountingRuleReadPlatformServiceImpl.java | 8 ++++---- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/api/AccountingRuleApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/api/AccountingRuleApiResource.java index 8173094bf..b3d414980 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/api/AccountingRuleApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/api/AccountingRuleApiResource.java @@ -45,6 +45,7 @@ import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSeria import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.organisation.office.data.OfficeData; import org.mifosplatform.organisation.office.service.OfficeReadPlatformService; +import org.mifosplatform.useradministration.domain.AppUser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -106,17 +107,21 @@ public class AccountingRuleApiResource { @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) public String retrieveAllAccountingRules(@Context final UriInfo uriInfo) { - - this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission); + + final AppUser currentUser = context.authenticatedUser(); + currentUser.validateHasReadPermission(this.resourceNameForPermission); + + final String hierarchy = currentUser.getOffice().getHierarchy(); + final String hierarchySearchString = hierarchy + "%"; + final Set associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); - final Long officeId = this.context.authenticatedUser().getOffice().getId(); boolean isAssociationParametersExists = false; if (!associationParameters.isEmpty()) { if (associationParameters.contains("all")) { isAssociationParametersExists = true; // If true, retrieve additional fields for journal entry form. } } - final List accountingRuleDatas = this.accountingRuleReadPlatformService.retrieveAllAccountingRules(officeId, + final List accountingRuleDatas = this.accountingRuleReadPlatformService.retrieveAllAccountingRules(hierarchySearchString, isAssociationParametersExists); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleReadPlatformService.java index d8d6ab4dd..f896531bb 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleReadPlatformService.java @@ -11,7 +11,7 @@ import org.mifosplatform.accounting.rule.data.AccountingRuleData; public interface AccountingRuleReadPlatformService { - List retrieveAllAccountingRules(Long OfficeId, boolean isAssociationParametersExists); + List retrieveAllAccountingRules(String hierarchySearchString, boolean isAssociationParametersExists); AccountingRuleData retrieveAccountingRuleById(Long accountingRuleId); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleReadPlatformServiceImpl.java index 6546a4b84..22fe7f30f 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleReadPlatformServiceImpl.java @@ -133,14 +133,14 @@ public class AccountingRuleReadPlatformServiceImpl implements AccountingRuleRead } @Override - public List retrieveAllAccountingRules(final Long officeId, final boolean isAssociationParametersExists) { + public List retrieveAllAccountingRules(final String hierarchySearchString, final boolean isAssociationParametersExists) { final AccountingRuleDataExtractor resultSetExtractor = new AccountingRuleDataExtractor(this.jdbcTemplate, this.glAccountReadPlatformService, isAssociationParametersExists); Object[] arguments = new Object[] {}; String sql = "select " + resultSetExtractor.schema() + " and system_defined=0 "; - if (officeId != null) { - sql = sql + " and office.id = ?"; - arguments = new Object[] { officeId }; + if (hierarchySearchString != null) { + sql = sql + " and office.hierarchy like ?"; + arguments = new Object[] { hierarchySearchString }; } sql = sql + " order by rule.id asc"; final Map extractedData = this.jdbcTemplate.query(sql, resultSetExtractor, arguments); From fb71820c5c2b25bdaf2b85397b233824c7f21f65 Mon Sep 17 00:00:00 2001 From: Vishwas Babu AJ Date: Sun, 23 Jun 2013 18:41:58 +0530 Subject: [PATCH 09/16] adding support for pentaho report localization --- api-docs/apiLive.htm | 6 + .../core/api/ApiParameterHelper.java | 13 +- .../core/serialization/JsonParserHelper.java | 12 +- .../api/RunreportsApiResource.java | 226 ++++++++---------- .../service/ReadReportingService.java | 21 +- .../service/ReadReportingServiceImpl.java | 38 +-- .../main/pentahoReports/Balance Sheet.prpt | Bin 22201 -> 22155 bytes .../main/pentahoReports/Income Statement.prpt | Bin 16948 -> 16919 bytes .../main/pentahoReports/Trial Balance.prpt | Bin 7912 -> 7883 bytes .../pentahoReports/balanceSheet.properties | 3 - .../pentahoReports/incomeStatement.properties | 4 +- .../pentahoReports/trialBalance.properties | 4 +- 12 files changed, 154 insertions(+), 173 deletions(-) diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index f8629176d..ed0637439 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -13419,6 +13419,12 @@ Request Body:
Valid Values are HTML, XLS, CSV and PDF for html, Excel, CSV and PDF formats respectively.
Only applies to Pentaho reports.
+
locale
+
+ optional +
+
Any valid locale Ex: en_US, en_IN, fr_FR etc
+
Only applies to Pentaho reports.

Example Requests:

runreports/Client%20Listing?R_officeId=1
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/ApiParameterHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/ApiParameterHelper.java index f599aa603..adec2c8f7 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/ApiParameterHelper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/ApiParameterHelper.java @@ -9,12 +9,14 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import javax.ws.rs.core.MultivaluedMap; import org.apache.commons.lang.StringUtils; +import org.mifosplatform.infrastructure.core.serialization.JsonParserHelper; public class ApiParameterHelper { @@ -62,6 +64,15 @@ public class ApiParameterHelper { return prettyPrint; } + public static Locale extractLocale(final MultivaluedMap queryParams) { + Locale locale = null; + if (queryParams.getFirst("locale") != null) { + String localeAsString = queryParams.getFirst("locale"); + locale = JsonParserHelper.localeFromString(localeAsString); + } + return locale; + } + public static boolean exportCsv(final MultivaluedMap queryParams) { boolean exportCsv = false; if (queryParams.getFirst("exportCSV") != null) { @@ -128,7 +139,7 @@ public class ApiParameterHelper { public static boolean genericResultSetPassed(MultivaluedMap queryParams) { return queryParams.getFirst("genericResultSet") != null; } - + public static String sqlEncodeString(String str) { String singleQuote = "'"; String twoSingleQuotes = "''"; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/serialization/JsonParserHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/serialization/JsonParserHelper.java index 875a31202..f3b51db00 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/serialization/JsonParserHelper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/serialization/JsonParserHelper.java @@ -518,7 +518,7 @@ public class JsonParserHelper { } } - private Locale localeFromString(final String localeAsString) { + public static Locale localeFromString(final String localeAsString) { if (StringUtils.isBlank(localeAsString)) { List dataValidationErrors = new ArrayList(); @@ -531,7 +531,7 @@ public class JsonParserHelper { } String languageCode = ""; - String courntryCode = ""; + String countryCode = ""; String variantCode = ""; String[] localeParts = localeAsString.split("_"); @@ -542,19 +542,19 @@ public class JsonParserHelper { if (localeParts != null && localeParts.length == 2) { languageCode = localeParts[0]; - courntryCode = localeParts[1]; + countryCode = localeParts[1]; } if (localeParts != null && localeParts.length == 3) { languageCode = localeParts[0]; - courntryCode = localeParts[1]; + countryCode = localeParts[1]; variantCode = localeParts[2]; } - return localeFrom(languageCode, courntryCode, variantCode); + return localeFrom(languageCode, countryCode, variantCode); } - private Locale localeFrom(final String languageCode, final String courntryCode, final String variantCode) { + private static Locale localeFrom(final String languageCode, final String courntryCode, final String variantCode) { List dataValidationErrors = new ArrayList(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/api/RunreportsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/api/RunreportsApiResource.java index bb5c20124..9a9b40dcf 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/api/RunreportsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/api/RunreportsApiResource.java @@ -7,6 +7,7 @@ package org.mifosplatform.infrastructure.dataqueries.api; import java.io.File; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -41,158 +42,123 @@ import org.springframework.stereotype.Component; @Scope("singleton") public class RunreportsApiResource { - private final PlatformSecurityContext context; - private final ToApiJsonSerializer toApiJsonSerializer; - private final ReadReportingService readExtraDataAndReportingService; - private final GenericDataService genericDataService; + private final PlatformSecurityContext context; + private final ToApiJsonSerializer toApiJsonSerializer; + private final ReadReportingService readExtraDataAndReportingService; + private final GenericDataService genericDataService; - @Autowired - public RunreportsApiResource(final PlatformSecurityContext context, - final ReadReportingService readExtraDataAndReportingService, - final GenericDataService genericDataService, - final ToApiJsonSerializer toApiJsonSerializer) { - this.context = context; - this.readExtraDataAndReportingService = readExtraDataAndReportingService; - this.genericDataService = genericDataService; - this.toApiJsonSerializer = toApiJsonSerializer; - } + @Autowired + public RunreportsApiResource(final PlatformSecurityContext context, final ReadReportingService readExtraDataAndReportingService, + final GenericDataService genericDataService, final ToApiJsonSerializer toApiJsonSerializer) { + this.context = context; + this.readExtraDataAndReportingService = readExtraDataAndReportingService; + this.genericDataService = genericDataService; + this.toApiJsonSerializer = toApiJsonSerializer; + } - @GET - @Path("{reportName}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON, "application/x-msdownload", - "application/vnd.ms-excel", "application/pdf", "text/html" }) - public Response runReport(@PathParam("reportName") final String reportName, - @Context final UriInfo uriInfo) { + @GET + @Path("{reportName}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON, "application/x-msdownload", "application/vnd.ms-excel", "application/pdf", "text/html" }) + public Response runReport(@PathParam("reportName") final String reportName, @Context final UriInfo uriInfo) { - MultivaluedMap queryParams = uriInfo - .getQueryParameters(); + MultivaluedMap queryParams = uriInfo.getQueryParameters(); - boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo - .getQueryParameters()); - boolean exportCsv = ApiParameterHelper.exportCsv(uriInfo - .getQueryParameters()); - boolean parameterType = ApiParameterHelper.parameterType(uriInfo - .getQueryParameters()); - boolean exportPdf = ApiParameterHelper.exportPdf(uriInfo - .getQueryParameters()); + boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); + boolean exportCsv = ApiParameterHelper.exportCsv(uriInfo.getQueryParameters()); + boolean parameterType = ApiParameterHelper.parameterType(uriInfo.getQueryParameters()); + boolean exportPdf = ApiParameterHelper.exportPdf(uriInfo.getQueryParameters()); - checkUserPermissionForReport(reportName, parameterType); + checkUserPermissionForReport(reportName, parameterType); - String parameterTypeValue = null; - if (!parameterType) { - parameterTypeValue = "report"; - if (this.readExtraDataAndReportingService.getReportType(reportName) - .equalsIgnoreCase("Pentaho")) { - Map reportParams = getReportParams(queryParams, - true); - return this.readExtraDataAndReportingService - .processPentahoRequest(reportName, - queryParams.getFirst("output-type"), - reportParams); - } - } else { - parameterTypeValue = "parameter"; - } + String parameterTypeValue = null; + if (!parameterType) { + parameterTypeValue = "report"; + if (this.readExtraDataAndReportingService.getReportType(reportName).equalsIgnoreCase("Pentaho")) { + Map reportParams = getReportParams(queryParams, true); + Locale locale = ApiParameterHelper.extractLocale(queryParams); + return this.readExtraDataAndReportingService.processPentahoRequest(reportName, queryParams.getFirst("output-type"), + reportParams, locale); + } + } else { + parameterTypeValue = "parameter"; + } - // PDF format + // PDF format - if (exportPdf) { - Map reportParams = getReportParams(queryParams, - false); - String pdfFileName = this.readExtraDataAndReportingService - .retrieveReportPDF(reportName, parameterTypeValue, - reportParams); + if (exportPdf) { + Map reportParams = getReportParams(queryParams, false); + String pdfFileName = this.readExtraDataAndReportingService.retrieveReportPDF(reportName, parameterTypeValue, reportParams); - File file = new File(pdfFileName); + File file = new File(pdfFileName); - ResponseBuilder response = Response.ok(file); - response.header("Content-Disposition", "attachment; filename=\"" - + pdfFileName + "\""); - response.header("content-Type", "application/pdf"); + ResponseBuilder response = Response.ok(file); + response.header("Content-Disposition", "attachment; filename=\"" + pdfFileName + "\""); + response.header("content-Type", "application/pdf"); - return response.build(); + return response.build(); - } + } - if (!exportCsv) { - Map reportParams = getReportParams(queryParams, - false); + if (!exportCsv) { + Map reportParams = getReportParams(queryParams, false); - GenericResultsetData result = this.readExtraDataAndReportingService - .retrieveGenericResultset(reportName, parameterTypeValue, - reportParams); + GenericResultsetData result = this.readExtraDataAndReportingService.retrieveGenericResultset(reportName, parameterTypeValue, + reportParams); - String json = ""; - final boolean genericResultSetIsPassed = ApiParameterHelper - .genericResultSetPassed(uriInfo.getQueryParameters()); - final boolean genericResultSet = ApiParameterHelper - .genericResultSet(uriInfo.getQueryParameters()); - if (genericResultSetIsPassed) { - if (genericResultSet) { - json = this.toApiJsonSerializer.serializePretty( - prettyPrint, result); - } else { - json = this.genericDataService - .generateJsonFromGenericResultsetData(result); - } - } else { - json = this.toApiJsonSerializer.serializePretty(prettyPrint, - result); - } + String json = ""; + final boolean genericResultSetIsPassed = ApiParameterHelper.genericResultSetPassed(uriInfo.getQueryParameters()); + final boolean genericResultSet = ApiParameterHelper.genericResultSet(uriInfo.getQueryParameters()); + if (genericResultSetIsPassed) { + if (genericResultSet) { + json = this.toApiJsonSerializer.serializePretty(prettyPrint, result); + } else { + json = this.genericDataService.generateJsonFromGenericResultsetData(result); + } + } else { + json = this.toApiJsonSerializer.serializePretty(prettyPrint, result); + } - return Response.ok().entity(json).type(MediaType.APPLICATION_JSON) - .build(); - } + return Response.ok().entity(json).type(MediaType.APPLICATION_JSON).build(); + } - // CSV Export - Map reportParams = getReportParams(queryParams, false); - StreamingOutput result = this.readExtraDataAndReportingService - .retrieveReportCSV(reportName, parameterTypeValue, reportParams); + // CSV Export + Map reportParams = getReportParams(queryParams, false); + StreamingOutput result = this.readExtraDataAndReportingService.retrieveReportCSV(reportName, parameterTypeValue, reportParams); - return Response - .ok() - .entity(result) - .type("application/x-msdownload") - .header("Content-Disposition", - "attachment;filename=" + reportName.replaceAll(" ", "") - + ".csv").build(); - } + return Response.ok().entity(result).type("application/x-msdownload") + .header("Content-Disposition", "attachment;filename=" + reportName.replaceAll(" ", "") + ".csv").build(); + } - private void checkUserPermissionForReport(final String reportName, - final boolean parameterType) { + private void checkUserPermissionForReport(final String reportName, final boolean parameterType) { - // Anyone can run a 'report' that is simply getting possible parameter - // (dropdown listbox) values. - if (!parameterType) { - AppUser currentUser = context.authenticatedUser(); - if (currentUser.hasNotPermissionForReport(reportName)) { - throw new NoAuthorizationException( - "Not authorised to run report: " + reportName); - } - } - } + // Anyone can run a 'report' that is simply getting possible parameter + // (dropdown listbox) values. + if (!parameterType) { + AppUser currentUser = context.authenticatedUser(); + if (currentUser.hasNotPermissionForReport(reportName)) { throw new NoAuthorizationException("Not authorised to run report: " + + reportName); } + } + } - private Map getReportParams( - final MultivaluedMap queryParams, - final Boolean isPentaho) { + private Map getReportParams(final MultivaluedMap queryParams, final Boolean isPentaho) { - Map reportParams = new HashMap(); - Set keys = queryParams.keySet(); - String pKey; - String pValue; - for (String k : keys) { + Map reportParams = new HashMap(); + Set keys = queryParams.keySet(); + String pKey; + String pValue; + for (String k : keys) { - if (k.startsWith("R_")) { - if (isPentaho) - pKey = k.substring(2); - else - pKey = "${" + k.substring(2) + "}"; + if (k.startsWith("R_")) { + if (isPentaho) + pKey = k.substring(2); + else + pKey = "${" + k.substring(2) + "}"; - pValue = queryParams.get(k).get(0); - reportParams.put(pKey, pValue); - } - } - return reportParams; - } + pValue = queryParams.get(k).get(0); + reportParams.put(pKey, pValue); + } + } + return reportParams; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadReportingService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadReportingService.java index f96e697d5..62c1bb572 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadReportingService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadReportingService.java @@ -6,6 +6,7 @@ package org.mifosplatform.infrastructure.dataqueries.service; import java.util.Collection; +import java.util.Locale; import java.util.Map; import javax.ws.rs.core.Response; @@ -17,23 +18,19 @@ import org.mifosplatform.infrastructure.dataqueries.data.ReportParameterData; public interface ReadReportingService { - StreamingOutput retrieveReportCSV(String name, String type, - Map extractedQueryParams); + StreamingOutput retrieveReportCSV(String name, String type, Map extractedQueryParams); - GenericResultsetData retrieveGenericResultset(String name, String type, - Map extractedQueryParams); + GenericResultsetData retrieveGenericResultset(String name, String type, Map extractedQueryParams); - Response processPentahoRequest(String reportName, String outputType, - Map queryParams); + Response processPentahoRequest(String reportName, String outputType, Map queryParams, Locale locale); - String retrieveReportPDF(String name, String type, - Map extractedQueryParams); + String retrieveReportPDF(String name, String type, Map extractedQueryParams); - String getReportType(String reportName); + String getReportType(String reportName); - Collection retrieveReportList(); + Collection retrieveReportList(); - Collection getAllowedParameters(); + Collection getAllowedParameters(); - ReportData retrieveReport(final Long id); + ReportData retrieveReport(final Long id); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadReportingServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadReportingServiceImpl.java index cace21ef3..a8c5ddcb6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadReportingServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadReportingServiceImpl.java @@ -19,6 +19,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -41,6 +42,7 @@ import org.mifosplatform.infrastructure.documentmanagement.contentrepository.Fil import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.useradministration.domain.AppUser; import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot; +import org.pentaho.reporting.engine.classic.core.DefaultReportEnvironment; import org.pentaho.reporting.engine.classic.core.MasterReport; import org.pentaho.reporting.engine.classic.core.ReportProcessingException; import org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.PdfReportUtil; @@ -231,7 +233,8 @@ public class ReadReportingServiceImpl implements ReadReportingService { } @Override - public Response processPentahoRequest(final String reportName, final String outputTypeParam, final Map queryParams) { + public Response processPentahoRequest(final String reportName, final String outputTypeParam, final Map queryParams, + final Locale locale) { String outputType = "HTML"; if (StringUtils.isNotBlank(outputTypeParam)) outputType = outputTypeParam; @@ -243,7 +246,8 @@ public class ReadReportingServiceImpl implements ReadReportingService { if (noPentaho) { throw new PlatformDataIntegrityException("error.msg.no.pentaho", "Pentaho is not enabled", "Pentaho is not enabled"); } - final String reportPath = FileSystemContentRepository.MIFOSX_BASE_DIR + File.separator + "pentahoReports" + File.separator + reportName + ".prpt"; + final String reportPath = FileSystemContentRepository.MIFOSX_BASE_DIR + File.separator + "pentahoReports" + File.separator + + reportName + ".prpt"; logger.info("Report path: " + reportPath); // load report definition @@ -254,7 +258,10 @@ public class ReadReportingServiceImpl implements ReadReportingService { try { res = manager.createDirectly(reportPath, MasterReport.class); MasterReport masterReport = (MasterReport) res.getResource(); - + DefaultReportEnvironment reportEnvironment = (DefaultReportEnvironment) masterReport.getReportEnvironment(); + if (locale != null) { + reportEnvironment.setLocale(locale); + } addParametersToReport(masterReport, queryParams); ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -336,12 +343,9 @@ public class ReadReportingServiceImpl implements ReadReportingService { // data scoping Connection connection = dataSource.getConnection(); String tenantdb; - try - { + try { tenantdb = connection.getCatalog(); - } - finally - { + } finally { connection.close(); } String userhierarchy = currentUser.getOffice().getHierarchy(); @@ -467,8 +471,8 @@ public class ReadReportingServiceImpl implements ReadReportingService { if (reportParameters == null) { reportParameters = new ArrayList(); } - reportParameters.add(new ReportParameterData(rpJoin.getReportParameterId(), rpJoin.getParameterId(), rpJoin.getReportParameterName(), rpJoin - .getParameterName())); + reportParameters.add(new ReportParameterData(rpJoin.getReportParameterId(), rpJoin.getParameterId(), rpJoin + .getReportParameterName(), rpJoin.getParameterName())); } else { if (firstReport) { @@ -494,8 +498,8 @@ public class ReadReportingServiceImpl implements ReadReportingService { if (rpJoin.getReportParameterId() != null) { // report has at least one parameter reportParameters = new ArrayList(); - reportParameters.add(new ReportParameterData(rpJoin.getReportParameterId(),rpJoin.getParameterId(), rpJoin.getReportParameterName(), rpJoin - .getParameterName())); + reportParameters.add(new ReportParameterData(rpJoin.getReportParameterId(), rpJoin.getParameterId(), rpJoin + .getReportParameterName(), rpJoin.getParameterName())); } else { reportParameters = null; } @@ -509,17 +513,15 @@ public class ReadReportingServiceImpl implements ReadReportingService { return reportList; } + @Override + public Collection getAllowedParameters() { - @Override - public Collection getAllowedParameters() { - ReportParameterMapper rm = new ReportParameterMapper(); String sql = rm.schema(); Collection parameters = this.jdbcTemplate.query(sql, rm, new Object[] {}); return parameters; - } - - + } + private static final class ReportParameterJoinMapper implements RowMapper { public String schema(final Long reportId) { diff --git a/mifosng-provider/src/main/pentahoReports/Balance Sheet.prpt b/mifosng-provider/src/main/pentahoReports/Balance Sheet.prpt index 566149fb6ed16dfdd3e22922e803cc0065cc7263..6d77a583e1d14ce0a7e82d740042ea91831e16c5 100644 GIT binary patch delta 12173 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 delta 12578 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 delta 8992 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 delta 5913 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 diff --git a/mifosng-provider/src/main/pentahoReports/balanceSheet.properties b/mifosng-provider/src/main/pentahoReports/balanceSheet.properties index c48f08c4c..5fd8cf174 100644 --- a/mifosng-provider/src/main/pentahoReports/balanceSheet.properties +++ b/mifosng-provider/src/main/pentahoReports/balanceSheet.properties @@ -11,6 +11,3 @@ totalEquities=Total Equities : branchName=Branch : asOn=As On : retainedEarnings=Retained Earnings -PrintedBy\:=Printed by: -On\:=On: -Page=Page \ No newline at end of file diff --git a/mifosng-provider/src/main/pentahoReports/incomeStatement.properties b/mifosng-provider/src/main/pentahoReports/incomeStatement.properties index ed0f68931..1fcfad6d7 100644 --- a/mifosng-provider/src/main/pentahoReports/incomeStatement.properties +++ b/mifosng-provider/src/main/pentahoReports/incomeStatement.properties @@ -8,8 +8,8 @@ expenses=Expenses totalExpenses=Total Expenses : netIncome=Net Income : branchName=Branch : -from=From : -to=To : +period=Period: +to=To PrintedBy\:=Printed by: On\:=On: Page=Page \ No newline at end of file diff --git a/mifosng-provider/src/main/pentahoReports/trialBalance.properties b/mifosng-provider/src/main/pentahoReports/trialBalance.properties index cd8151fc3..1ab1bc42d 100644 --- a/mifosng-provider/src/main/pentahoReports/trialBalance.properties +++ b/mifosng-provider/src/main/pentahoReports/trialBalance.properties @@ -9,4 +9,6 @@ name=Name total=Total : PrintedBy\:=Printed by: On\:=On: -Page=Page \ No newline at end of file +Page=Page +period=Period: +to=To \ No newline at end of file From a71dea07667c0e501f2141bb8509c94edf6a2389 Mon Sep 17 00:00:00 2001 From: Vishwas Babu AJ Date: Sun, 23 Jun 2013 18:58:44 +0530 Subject: [PATCH 10/16] localizing income statement report --- .../main/pentahoReports/Income Statement.prpt | Bin 16919 -> 16919 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/mifosng-provider/src/main/pentahoReports/Income Statement.prpt b/mifosng-provider/src/main/pentahoReports/Income Statement.prpt index 7bc532dfda3f8200221905f0beabe7b865c9be5c..050b62989888fbf2901b7176e48373f99bf68a9e 100644 GIT binary patch delta 854 zcmbQ5TR7rd@Xei$VzwAf209lBidao3l0K zZdS8_$iDZ%Ebo1Lk}{jO_H3Mbw`XH1|AeE#vln`LB;9;`ety)OQ`_!|>^XVz=RZF6 zwAbr*Ik9i>%i7AN(Z|H%^}=aYk?5X$`NOUo&ZW=1_jUopRZ`uI^I^l zI`l%sQIqmZhDWmt&Tuc|T_JdA+1)kbQd+WCx4u|c-lcm)>qd3HgyOP|qSv;mv?>HR zvKO@^c{SCvU65Fy{Nh~h(&t)^jZfol|5?iVVXxczge_llYrZV-TFG!X{Enq&K-2UY zZL_U?o=-U@`Ll8U1TOA7+duPgzf!$EGPpyIlP9Zz~_HeD82bdcFCA zd9v0X>h@X|%WSXuFMOLJHv3?mePdly8+QTAFCG^j$*PlK9h39gc5dfQdE)f;Q0vBH z?dKW}9Ctlv`v1x|w{??NE{$KZext_KvpRbJH{S4!PrO*5|IK`p$Nz0NyybPpntHou zwOq96nVf5qZkT!M(a{YnlAXWzow(_2C40V(=jR*g0B?4VKO0Mg$`~0K!dVy?KyfF+ zzyXBsrcGYJ{2ds1n`g2FGlS@N>@18xdh!>J10e1;&aW&WdM)2UR*-Ok$ZrrgS2Q22 z`kn+MNcHB2lIy^_CQ7@2R9%-o1rl8>Bg+Qj3aZO&zNcgeHb_uin+YN&J9)k)=j0Sk z8IaN@O>VG3{aTm72BqlufmChLxdM{y($xbQw0W1YB*>7-yNuN~|2CHb8zgSE2qd)I z$^=Axv~uJCr9qH)CtF$TfyA<`%|X;+YdaA2*xCw2$=kSts0etW zwqRbEts{sx)7Aq-J+=j_RJRKR@v7{6K-3942N1<&52kz|RG+;!Nbt6O5Qx%u0IRHZ Xhz9X)IfQ{IBS$X~RR^I?Ir;(s80>&d delta 854 zcmbQ>5TQar(Aaei$VzwAf209lBidao3l0K zu3xi($iDl*CU-6e6?A7e9F>UOW9asr`AX5TtOdEZHvRbV$yn^!qIYSW=C-!?@1N9s z`D*pL3vz2FN2M|z4Qz1WjP(djJNWyV;U3uN67Kr8`S%3V-$onXLg&Zxqx| zW$g|1DOQ&a-oti$Pr|JRDaD*yXR8)2yc8JQ^{#694X2cY=c*5L81}B+uuWByrIE{O zg0e`k&=cd9V+^v6<$2n+;hbDL-)}wB*KK%jQpLJ>ZT{oM?4e$)FZMk;=h-N<=ZHzn z{lr}hO#Dvr|7i({c(8A7dPD(#rOZW(oc$U5>N_=N2fIyw@iMnq!Q!o~N5$i>Qdc%7 zSHFDX&f31^_%FjPvS*WwZ@gE(;D4cjvCm*h-s~LK7OKC>7#SGCSr`~VaVNsS z0fe`wOkTkJ9T<6=XR-t{gXnkcEQ~;U@)wQ+AnrEKuPh*XE#E;_kZ^&>ZxA%h7uO1pqmU6(!u5?w4K%Ld{Ks>^J?r(_5=NKjpy2_hytdA=s+c9T9?5FrRey9RBh3@0+Q|0)dLx{d6%&y$dJjqjMX;(HkSe$ByP0`B(&Sg z1Vnwba^wJ|L6CPRTUqOY#Imf-LDXVvI}r8Q+6qL;+qi?M3>zm9wZ_H_L_M*w08w(b zU|yK5BZxQC)&oR6wgszHw+jUEs_c9~)CoHW5XEH=rhFh&pS?Fo@V0#rh|+fetE_d1 W2Jvn=gn=j{M=uan2cb?m`T_td!**~0 From bde74b8e24495743361adcb860e4f7f88bf94154 Mon Sep 17 00:00:00 2001 From: Vishwas Babu AJ Date: Mon, 24 Jun 2013 10:00:02 +0530 Subject: [PATCH 11/16] adding locale based am/pm to time in accountig pentaho reports --- .../main/pentahoReports/Balance Sheet.prpt | Bin 22155 -> 22094 bytes .../main/pentahoReports/Income Statement.prpt | Bin 16919 -> 16882 bytes .../main/pentahoReports/Trial Balance.prpt | Bin 7883 -> 7882 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/mifosng-provider/src/main/pentahoReports/Balance Sheet.prpt b/mifosng-provider/src/main/pentahoReports/Balance Sheet.prpt index 6d77a583e1d14ce0a7e82d740042ea91831e16c5..5511e41c562142f41d953f1ae96e16d8f5c289aa 100644 GIT binary patch delta 8300 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 delta 8442 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 diff --git a/mifosng-provider/src/main/pentahoReports/Trial Balance.prpt b/mifosng-provider/src/main/pentahoReports/Trial Balance.prpt index 749f761054fb30e554ab80830ae9e76c719b756f..5ffa662a1865f69791427915d35bc813d88c7fd7 100644 GIT binary patch delta 3523 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 T^coDS4})S~Am}M3&tKs`()&)m delta 3546 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 Wf)$1@4nym~n9vohltcrU2m2pg!$oKS From 58eda883270af1541def3cb1affcf870a7cc0747 Mon Sep 17 00:00:00 2001 From: mifoscontributer Date: Mon, 24 Jun 2013 18:48:17 +0530 Subject: [PATCH 12/16] Allow assign parent office staff to group Fixing missed fun --- .../mifosplatform/organisation/staff/domain/Staff.java | 4 ++++ .../organisation/staff/domain/StaffRepository.java | 2 -- .../staff/domain/StaffRepositoryWrapper.java | 10 ++++++++++ ...pingTypesWritePlatformServiceJpaRepositoryImpl.java | 4 ++-- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/Staff.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/Staff.java index 69d4c2df4..22f7b2879 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/Staff.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/Staff.java @@ -162,4 +162,8 @@ public class Staff extends AbstractPersistable { public String displayName() { return this.displayName; } + + public Office office() { + return this.office; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/StaffRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/StaffRepository.java index 2294d04b8..39fbbc5b6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/StaffRepository.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/StaffRepository.java @@ -20,7 +20,5 @@ public interface StaffRepository extends JpaRepository, */ @Query(FIND_BY_OFFICE_QUERY) public Staff findByOffice(@Param("id") Long id, @Param("officeId") Long officeId); - - } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/StaffRepositoryWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/StaffRepositoryWrapper.java index 536bab369..acc3ff8f1 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/StaffRepositoryWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/StaffRepositoryWrapper.java @@ -36,4 +36,14 @@ public class StaffRepositoryWrapper { if (staff == null) { throw new StaffNotFoundException(staffId); } return staff; } + + public Staff findByOfficeHierarchyWithNotFoundDetection(final Long staffId, final String hierarchy) { + final Staff staff = this.repository.findOne(staffId); + if (staff == null) { throw new StaffNotFoundException(staffId); } + final String staffhierarchy = staff.office().getHierarchy(); + if (!hierarchy.startsWith(staffhierarchy)) { + throw new StaffNotFoundException(staffId); + } + return staff; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformServiceJpaRepositoryImpl.java index 3cc819aad..657392a50 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformServiceJpaRepositoryImpl.java @@ -109,7 +109,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group Staff staff = null; final Long staffId = command.longValueOfParameterNamed(GroupingTypesApiConstants.staffIdParamName); if (staffId != null) { - staff = this.staffRepository.findByOfficeWithNotFoundDetection(staffId, officeId); + staff = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(staffId, groupOffice.getHierarchy()); } final Set clientMembers = assembleSetOfClients(officeId, command); @@ -238,7 +238,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group Staff newStaff = null; if (newValue != null) { - newStaff = this.staffRepository.findByOfficeWithNotFoundDetection(newValue, officeId); + newStaff = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(newValue, groupHierarchy); } groupForUpdate.updateStaff(newStaff); } From fda126474cb18b8ed0da7213a4d4c790261dc59d Mon Sep 17 00:00:00 2001 From: Vishwas Babu AJ Date: Tue, 25 Jun 2013 15:23:45 +0530 Subject: [PATCH 13/16] datatable delete apis should only delete registered datatables --- .../ReadWriteNonCoreDataServiceImpl.java | 604 +++++++++--------- 1 file changed, 296 insertions(+), 308 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java index d19c35c46..f79c73b85 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java @@ -58,13 +58,20 @@ import com.google.gson.reflect.TypeToken; @Service public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataService { - private final static String DATATABLE_NAME_REGEX_PATTERN = "^[a-zA-Z][a-zA-Z0-9\\-_\\s]{0,48}[a-zA-Z0-9]$"; + private final static String DATATABLE_NAME_REGEX_PATTERN = "^[a-zA-Z][a-zA-Z0-9\\-_\\s]{0,48}[a-zA-Z0-9]$"; private final static Logger logger = LoggerFactory.getLogger(ReadWriteNonCoreDataServiceImpl.class); - private final static HashMap apiTypeToMySQL = new HashMap() {{ - put("String", "VARCHAR"); put("Number", "INT"); put("Decimal", "DECIMAL"); put("Date", "DATE"); - put("Text", "TEXT"); put ("Dropdown", "INT"); - }}; + private final static HashMap apiTypeToMySQL = new HashMap() { + + { + put("String", "VARCHAR"); + put("Number", "INT"); + put("Decimal", "DECIMAL"); + put("Date", "DATE"); + put("Text", "TEXT"); + put("Dropdown", "INT"); + } + }; private final JdbcTemplate jdbcTemplate; private final DataSource dataSource; @@ -282,396 +289,377 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ } } + private boolean isRegisteredDataTable(final String name) { + // PERMITTED datatables + final String sql = "select if((exists (select 1 from x_registered_table where registered_table_name = ?)) = 1, 'true', 'false')"; + final String isRegisteredDataTable = this.jdbcTemplate.queryForObject(sql, String.class, new Object[] { name }); + return new Boolean(isRegisteredDataTable); + } + private void validateDatatableName(final String name) { - - if (name == null || name.isEmpty()) { - throw new PlatformDataIntegrityException("error.msg.datatables.datatable.null.name", - "Data table name must not be blank."); - } else if (!name.matches(DATATABLE_NAME_REGEX_PATTERN)) { - throw new PlatformDataIntegrityException("error.msg.datatables.datatable.invalid.name.regex", - "Invalid data table name.", name); - } + + if (name == null || name.isEmpty()) { + throw new PlatformDataIntegrityException("error.msg.datatables.datatable.null.name", "Data table name must not be blank."); + } else if (!name.matches(DATATABLE_NAME_REGEX_PATTERN)) { throw new PlatformDataIntegrityException( + "error.msg.datatables.datatable.invalid.name.regex", "Invalid data table name.", name); } } private String datatableColumnNameToCodeValueName(final String columnName, final String code) { - return (code + "_cd_" + columnName); + return (code + "_cd_" + columnName); } private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); } } - + private void parseDatatableColumnObjectForCreate(final JsonObject column, StringBuilder sqlBuilder) { - - String name = (column.has("name")) ? column.get("name").getAsString() : null; - String type = (column.has("type")) ? column.get("type").getAsString() : null; - Integer length = (column.has("length")) ? column.get("length").getAsInt() : null; - Boolean mandatory = (column.has("mandatory")) ? column.get("mandatory").getAsBoolean() : false; - String code = (column.has("code")) ? column.get("code").getAsString() : null; - if (StringUtils.isNotBlank(code)) { - name = datatableColumnNameToCodeValueName(name, code); - } + String name = (column.has("name")) ? column.get("name").getAsString() : null; + String type = (column.has("type")) ? column.get("type").getAsString() : null; + Integer length = (column.has("length")) ? column.get("length").getAsInt() : null; + Boolean mandatory = (column.has("mandatory")) ? column.get("mandatory").getAsBoolean() : false; + String code = (column.has("code")) ? column.get("code").getAsString() : null; - String mysqlType = apiTypeToMySQL.get(type); - sqlBuilder = sqlBuilder.append("`" + name + "` " + mysqlType); + if (StringUtils.isNotBlank(code)) { + name = datatableColumnNameToCodeValueName(name, code); + } - if (type != null) - { - if (type.equals("String")) { - sqlBuilder = sqlBuilder.append("(" + length + ")"); - } else if (type.equals("Decimal")) { - sqlBuilder = sqlBuilder.append("(19,6)"); - } else if (type.equals("Dropdown")) { - sqlBuilder = sqlBuilder.append("(11)"); - } - } - if (mandatory != null) { - if (mandatory) { - sqlBuilder = sqlBuilder.append(" NOT NULL"); - } else { - sqlBuilder = sqlBuilder.append(" DEFAULT NULL"); - } - } + String mysqlType = apiTypeToMySQL.get(type); + sqlBuilder = sqlBuilder.append("`" + name + "` " + mysqlType); - sqlBuilder = sqlBuilder.append(", "); + if (type != null) { + if (type.equals("String")) { + sqlBuilder = sqlBuilder.append("(" + length + ")"); + } else if (type.equals("Decimal")) { + sqlBuilder = sqlBuilder.append("(19,6)"); + } else if (type.equals("Dropdown")) { + sqlBuilder = sqlBuilder.append("(11)"); + } + } + if (mandatory != null) { + if (mandatory) { + sqlBuilder = sqlBuilder.append(" NOT NULL"); + } else { + sqlBuilder = sqlBuilder.append(" DEFAULT NULL"); + } + } + + sqlBuilder = sqlBuilder.append(", "); } @Transactional @Override public CommandProcessingResult createDatatable(final JsonCommand command) { - String datatableName = null; + String datatableName = null; - try { - context.authenticatedUser(); - this.fromApiJsonDeserializer.validateForCreate(command.json()); + try { + context.authenticatedUser(); + this.fromApiJsonDeserializer.validateForCreate(command.json()); - JsonElement element = this.fromJsonHelper.parse(command.json()); - JsonArray columns = this.fromJsonHelper.extractJsonArrayNamed("columns", element); - datatableName = this.fromJsonHelper.extractStringNamed("datatableName", element); - String apptableName = this.fromJsonHelper.extractStringNamed("apptableName", element); - Boolean multiRow = this.fromJsonHelper.extractBooleanNamed("multiRow", element); + JsonElement element = this.fromJsonHelper.parse(command.json()); + JsonArray columns = this.fromJsonHelper.extractJsonArrayNamed("columns", element); + datatableName = this.fromJsonHelper.extractStringNamed("datatableName", element); + String apptableName = this.fromJsonHelper.extractStringNamed("apptableName", element); + Boolean multiRow = this.fromJsonHelper.extractBooleanNamed("multiRow", element); - if (multiRow == null) { - multiRow = false; - } + if (multiRow == null) { + multiRow = false; + } - validateDatatableName(datatableName); - validateAppTable(apptableName); + validateDatatableName(datatableName); + validateAppTable(apptableName); - String fkColumnName = apptableName.substring(2) + "_id"; - String fkName = datatableName.toLowerCase().replaceAll("\\s", "_") + "_" + fkColumnName; - StringBuilder sqlBuilder = new StringBuilder(); - sqlBuilder = sqlBuilder.append("CREATE TABLE `" + datatableName + "` ("); + String fkColumnName = apptableName.substring(2) + "_id"; + String fkName = datatableName.toLowerCase().replaceAll("\\s", "_") + "_" + fkColumnName; + StringBuilder sqlBuilder = new StringBuilder(); + sqlBuilder = sqlBuilder.append("CREATE TABLE `" + datatableName + "` ("); - if (multiRow) { - sqlBuilder = sqlBuilder - .append("`id` BIGINT(20) NOT NULL AUTO_INCREMENT, ") - .append("`" + fkColumnName + "` BIGINT(20) NOT NULL, "); - } else { - sqlBuilder = sqlBuilder - .append("`" + fkColumnName + "` BIGINT(20) NOT NULL, "); - } + if (multiRow) { + sqlBuilder = sqlBuilder.append("`id` BIGINT(20) NOT NULL AUTO_INCREMENT, ").append( + "`" + fkColumnName + "` BIGINT(20) NOT NULL, "); + } else { + sqlBuilder = sqlBuilder.append("`" + fkColumnName + "` BIGINT(20) NOT NULL, "); + } - for (JsonElement column : columns) { - parseDatatableColumnObjectForCreate(column.getAsJsonObject(), sqlBuilder); - } + for (JsonElement column : columns) { + parseDatatableColumnObjectForCreate(column.getAsJsonObject(), sqlBuilder); + } - // Remove trailing comma and space - sqlBuilder = sqlBuilder.delete(sqlBuilder.length() - 2, sqlBuilder.length()); + // Remove trailing comma and space + sqlBuilder = sqlBuilder.delete(sqlBuilder.length() - 2, sqlBuilder.length()); - if (multiRow) { - sqlBuilder = sqlBuilder - .append(", PRIMARY KEY (`id`)") - .append(", KEY `fk_" + apptableName.substring(2) + "_id` (`" + fkColumnName + "`)") - .append(", CONSTRAINT `fk_" + fkName + "` ") - .append("FOREIGN KEY (`" + fkColumnName + "`) ") - .append("REFERENCES `" + apptableName + "` (`id`)"); - } else { - sqlBuilder = sqlBuilder - .append(", PRIMARY KEY (`" + fkColumnName + "`)") - .append(", CONSTRAINT `fk_" + fkName + "` ") - .append("FOREIGN KEY (`" + fkColumnName + "`) ") - .append("REFERENCES `" + apptableName + "` (`id`)"); - } + if (multiRow) { + sqlBuilder = sqlBuilder.append(", PRIMARY KEY (`id`)") + .append(", KEY `fk_" + apptableName.substring(2) + "_id` (`" + fkColumnName + "`)") + .append(", CONSTRAINT `fk_" + fkName + "` ").append("FOREIGN KEY (`" + fkColumnName + "`) ") + .append("REFERENCES `" + apptableName + "` (`id`)"); + } else { + sqlBuilder = sqlBuilder.append(", PRIMARY KEY (`" + fkColumnName + "`)").append(", CONSTRAINT `fk_" + fkName + "` ") + .append("FOREIGN KEY (`" + fkColumnName + "`) ").append("REFERENCES `" + apptableName + "` (`id`)"); + } - sqlBuilder = sqlBuilder.append(") ENGINE=InnoDB DEFAULT CHARSET=utf8;"); - this.jdbcTemplate.execute(sqlBuilder.toString()); + sqlBuilder = sqlBuilder.append(") ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + this.jdbcTemplate.execute(sqlBuilder.toString()); + + registerDatatable(datatableName, apptableName); + } catch (SQLGrammarException e) { + Throwable realCause = e.getCause(); + List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("datatable"); + + if (realCause.getMessage().toLowerCase().contains("duplicate column name")) { + baseDataValidator.reset().parameter("name").failWithCode("duplicate.column.name"); + } else if (realCause.getMessage().contains("Table") && realCause.getMessage().contains("already exists")) { + baseDataValidator.reset().parameter("datatableName").value(datatableName).failWithCode("datatable.already.exists"); + } - registerDatatable(datatableName, apptableName); - } catch (SQLGrammarException e) { - Throwable realCause = e.getCause(); - List dataValidationErrors = new ArrayList(); - final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("datatable"); - - if (realCause.getMessage().toLowerCase().contains("duplicate column name")) { - baseDataValidator.reset().parameter("name").failWithCode("duplicate.column.name"); - } else if (realCause.getMessage().contains("Table") && realCause.getMessage().contains("already exists")) { - baseDataValidator.reset().parameter("datatableName").value(datatableName) - .failWithCode("datatable.already.exists"); - } - throwExceptionIfValidationWarningsExist(dataValidationErrors); - } + } - return new CommandProcessingResultBuilder() - .withCommandId(command.commandId()) - .withResourceIdAsString(datatableName) - .build(); + return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withResourceIdAsString(datatableName).build(); } private void parseDatatableColumnForUpdate(final JsonObject column, - final Map mapColumnNameDefinition, StringBuilder sqlBuilder) { + final Map mapColumnNameDefinition, StringBuilder sqlBuilder) { - String name = (column.has("name")) ? column.get("name").getAsString() : null; - String lengthStr = (column.has("length")) ? column.get("length").getAsString() : null; - Integer length = (StringUtils.isNotBlank(lengthStr)) ? Integer.parseInt(lengthStr) : null; - String newName = (column.has("newName")) ? column.get("newName").getAsString() : name; - Boolean mandatory = (column.has("mandatory")) ? column.get("mandatory").getAsBoolean() : false; - String after = (column.has("after")) ? column.get("after").getAsString() : null; - String code = (column.has("code")) ? column.get("code").getAsString() : null; - String newCode = (column.has("newCode")) ? column.get("newCode").getAsString() : null; + String name = (column.has("name")) ? column.get("name").getAsString() : null; + String lengthStr = (column.has("length")) ? column.get("length").getAsString() : null; + Integer length = (StringUtils.isNotBlank(lengthStr)) ? Integer.parseInt(lengthStr) : null; + String newName = (column.has("newName")) ? column.get("newName").getAsString() : name; + Boolean mandatory = (column.has("mandatory")) ? column.get("mandatory").getAsBoolean() : false; + String after = (column.has("after")) ? column.get("after").getAsString() : null; + String code = (column.has("code")) ? column.get("code").getAsString() : null; + String newCode = (column.has("newCode")) ? column.get("newCode").getAsString() : null; - if (StringUtils.isNotBlank(code)) { - name = datatableColumnNameToCodeValueName(name, code); + if (StringUtils.isNotBlank(code)) { + name = datatableColumnNameToCodeValueName(name, code); - if (StringUtils.isNotBlank(newCode)) { - newName = datatableColumnNameToCodeValueName(newName, newCode); - } else { - newName = datatableColumnNameToCodeValueName(newName, code); - } - } + if (StringUtils.isNotBlank(newCode)) { + newName = datatableColumnNameToCodeValueName(newName, newCode); + } else { + newName = datatableColumnNameToCodeValueName(newName, code); + } + } - if (!mapColumnNameDefinition.containsKey(name)) { - throw new PlatformDataIntegrityException("error.msg.datatable.column.missing.update.parse", - "Column " + name + " does not exist.", name); - } + if (!mapColumnNameDefinition.containsKey(name)) { throw new PlatformDataIntegrityException( + "error.msg.datatable.column.missing.update.parse", "Column " + name + " does not exist.", name); } - String type = mapColumnNameDefinition.get(name).getColumnType(); - if (length == null && type.toLowerCase().equals("varchar")) { - length = mapColumnNameDefinition.get(name).getColumnLength().intValue(); - } + String type = mapColumnNameDefinition.get(name).getColumnType(); + if (length == null && type.toLowerCase().equals("varchar")) { + length = mapColumnNameDefinition.get(name).getColumnLength().intValue(); + } - sqlBuilder = sqlBuilder.append(", CHANGE `" + name + "` `" + newName + "` " + type); - if (length != null && length > 0) { - if (type.toLowerCase().equals("decimal")) { - sqlBuilder.append("(19,6)"); - } else if (type.toLowerCase().equals("varchar")) { - sqlBuilder.append("(" + length + ")"); - } - } + sqlBuilder = sqlBuilder.append(", CHANGE `" + name + "` `" + newName + "` " + type); + if (length != null && length > 0) { + if (type.toLowerCase().equals("decimal")) { + sqlBuilder.append("(19,6)"); + } else if (type.toLowerCase().equals("varchar")) { + sqlBuilder.append("(" + length + ")"); + } + } - if (mandatory != null) { - if (mandatory) { - sqlBuilder = sqlBuilder.append(" NOT NULL"); - } else { - sqlBuilder = sqlBuilder.append(" DEFAULT NULL"); - } - } - if (after != null) { - sqlBuilder = sqlBuilder.append(" AFTER `" + after + "`"); - } + if (mandatory != null) { + if (mandatory) { + sqlBuilder = sqlBuilder.append(" NOT NULL"); + } else { + sqlBuilder = sqlBuilder.append(" DEFAULT NULL"); + } + } + if (after != null) { + sqlBuilder = sqlBuilder.append(" AFTER `" + after + "`"); + } } private void parseDatatableColumnForAdd(final JsonObject column, StringBuilder sqlBuilder) { - String name = (column.has("name")) ? column.get("name").getAsString() : null; - String type = (column.has("type")) ? column.get("type").getAsString() : null; - Integer length = (column.has("length")) ? column.get("length").getAsInt() : null; - Boolean mandatory = (column.has("mandatory")) ? column.get("mandatory").getAsBoolean() : false; - String after = (column.has("after")) ? column.get("after").getAsString() : null; - String code = (column.has("code")) ? column.get("code").getAsString() : null; + String name = (column.has("name")) ? column.get("name").getAsString() : null; + String type = (column.has("type")) ? column.get("type").getAsString() : null; + Integer length = (column.has("length")) ? column.get("length").getAsInt() : null; + Boolean mandatory = (column.has("mandatory")) ? column.get("mandatory").getAsBoolean() : false; + String after = (column.has("after")) ? column.get("after").getAsString() : null; + String code = (column.has("code")) ? column.get("code").getAsString() : null; - if (StringUtils.isNotBlank(code)) { - name = datatableColumnNameToCodeValueName(name, code); - } + if (StringUtils.isNotBlank(code)) { + name = datatableColumnNameToCodeValueName(name, code); + } - String mysqlType = apiTypeToMySQL.get(type); - sqlBuilder = sqlBuilder.append(", ADD `" + name + "` " + mysqlType); + String mysqlType = apiTypeToMySQL.get(type); + sqlBuilder = sqlBuilder.append(", ADD `" + name + "` " + mysqlType); - if (type != null) { - if (type.equals("String") && length != null) { - sqlBuilder = sqlBuilder.append("(" + length + ")"); - } else if (type.equals("Decimal")) { - sqlBuilder = sqlBuilder.append("(19,6)"); - } else if (type.equals("Dropdown")) { - sqlBuilder = sqlBuilder.append("(11)"); - } - } - if (mandatory != null) { - if (mandatory) { - sqlBuilder = sqlBuilder.append(" NOT NULL"); - } else { - sqlBuilder = sqlBuilder.append(" DEFAULT NULL"); - } - } - if (after != null) { - sqlBuilder = sqlBuilder.append(" AFTER `" + after + "`"); - } + if (type != null) { + if (type.equals("String") && length != null) { + sqlBuilder = sqlBuilder.append("(" + length + ")"); + } else if (type.equals("Decimal")) { + sqlBuilder = sqlBuilder.append("(19,6)"); + } else if (type.equals("Dropdown")) { + sqlBuilder = sqlBuilder.append("(11)"); + } + } + if (mandatory != null) { + if (mandatory) { + sqlBuilder = sqlBuilder.append(" NOT NULL"); + } else { + sqlBuilder = sqlBuilder.append(" DEFAULT NULL"); + } + } + if (after != null) { + sqlBuilder = sqlBuilder.append(" AFTER `" + after + "`"); + } } - private void parseDatatableColumnForDrop(final JsonObject column, StringBuilder sqlBuilder) { + private void parseDatatableColumnForDrop(final JsonObject column, StringBuilder sqlBuilder) { - String name = (column.has("name")) ? column.get("name").getAsString() : null; + String name = (column.has("name")) ? column.get("name").getAsString() : null; - sqlBuilder = sqlBuilder.append(", DROP COLUMN `" + name + "`"); - } + sqlBuilder = sqlBuilder.append(", DROP COLUMN `" + name + "`"); + } @Transactional @Override public void updateDatatable(final String datatableName, final JsonCommand command) { - try { - context.authenticatedUser(); - this.fromApiJsonDeserializer.validateForUpdate(command.json()); + try { + context.authenticatedUser(); + this.fromApiJsonDeserializer.validateForUpdate(command.json()); - JsonElement element = this.fromJsonHelper.parse(command.json()); - JsonArray changeColumns = this.fromJsonHelper.extractJsonArrayNamed("changeColumns", element); - JsonArray addColumns = this.fromJsonHelper.extractJsonArrayNamed("addColumns", element); - JsonArray dropColumns = this.fromJsonHelper.extractJsonArrayNamed("dropColumns", element); - String apptableName = this.fromJsonHelper.extractStringNamed("apptableName", element); + JsonElement element = this.fromJsonHelper.parse(command.json()); + JsonArray changeColumns = this.fromJsonHelper.extractJsonArrayNamed("changeColumns", element); + JsonArray addColumns = this.fromJsonHelper.extractJsonArrayNamed("addColumns", element); + JsonArray dropColumns = this.fromJsonHelper.extractJsonArrayNamed("dropColumns", element); + String apptableName = this.fromJsonHelper.extractStringNamed("apptableName", element); - validateDatatableName(datatableName); - - List columnHeaderData = this.genericDataService.fillResultsetColumnHeaders(datatableName); - Map mapColumnNameDefinition = new HashMap(); - for (ResultsetColumnHeaderData columnHeader : columnHeaderData) { - mapColumnNameDefinition.put(columnHeader.getColumnName(), columnHeader); - } + validateDatatableName(datatableName); - if (!StringUtils.isBlank(apptableName)) { - validateAppTable(apptableName); + List columnHeaderData = this.genericDataService.fillResultsetColumnHeaders(datatableName); + Map mapColumnNameDefinition = new HashMap(); + for (ResultsetColumnHeaderData columnHeader : columnHeaderData) { + mapColumnNameDefinition.put(columnHeader.getColumnName(), columnHeader); + } - String oldApptableName = this.queryForApplicationTableName(datatableName); - if (!StringUtils.equals(oldApptableName, apptableName)) { - String oldFKName = oldApptableName.substring(2) + "_id"; - String newFKName = apptableName.substring(2) + "_id"; - String oldConstraintName = datatableName.toLowerCase().replaceAll("\\s", "_") + "_" + oldFKName; - String newConstraintName = datatableName.toLowerCase().replaceAll("\\s", "_") + "_" + newFKName; - StringBuilder sqlBuilder = new StringBuilder(); - - if (mapColumnNameDefinition.containsKey("id")) { - sqlBuilder = sqlBuilder - .append("ALTER TABLE `" + datatableName + "` ") - .append("DROP KEY `fk_" + oldFKName + "`,") - .append("DROP FOREIGN KEY `fk_" + oldConstraintName + "`,") - .append("CHANGE COLUMN `" + oldFKName + "` `" + newFKName + "` BIGINT(20) NOT NULL,") - .append("ADD KEY `fk_" + newFKName + "` (`" + newFKName + "`),") - .append("ADD CONSTRAINT `fk_" + newConstraintName + "` ") - .append("FOREIGN KEY (`" + newFKName + "`) ") - .append("REFERENCES `" + apptableName + "` (`id`)"); - } else { - sqlBuilder = sqlBuilder - .append("ALTER TABLE `" + datatableName + "` ") - .append("DROP FOREIGN KEY `fk_" + oldConstraintName + "`,") - .append("CHANGE COLUMN `" + oldFKName + "` `" + newFKName + "` BIGINT(20) NOT NULL,") - .append("ADD CONSTRAINT `fk_" + newConstraintName + "` ") - .append("FOREIGN KEY (`" + newFKName + "`) ") - .append("REFERENCES `" + apptableName + "` (`id`)"); - } - - this.jdbcTemplate.execute(sqlBuilder.toString()); - - deregisterDatatable(datatableName); - registerDatatable(datatableName, apptableName); - } - } + if (!StringUtils.isBlank(apptableName)) { + validateAppTable(apptableName); - if (changeColumns == null && addColumns == null && dropColumns == null) { - return; - } + String oldApptableName = this.queryForApplicationTableName(datatableName); + if (!StringUtils.equals(oldApptableName, apptableName)) { + String oldFKName = oldApptableName.substring(2) + "_id"; + String newFKName = apptableName.substring(2) + "_id"; + String oldConstraintName = datatableName.toLowerCase().replaceAll("\\s", "_") + "_" + oldFKName; + String newConstraintName = datatableName.toLowerCase().replaceAll("\\s", "_") + "_" + newFKName; + StringBuilder sqlBuilder = new StringBuilder(); - if (dropColumns != null) { + if (mapColumnNameDefinition.containsKey("id")) { + sqlBuilder = sqlBuilder.append("ALTER TABLE `" + datatableName + "` ").append("DROP KEY `fk_" + oldFKName + "`,") + .append("DROP FOREIGN KEY `fk_" + oldConstraintName + "`,") + .append("CHANGE COLUMN `" + oldFKName + "` `" + newFKName + "` BIGINT(20) NOT NULL,") + .append("ADD KEY `fk_" + newFKName + "` (`" + newFKName + "`),") + .append("ADD CONSTRAINT `fk_" + newConstraintName + "` ").append("FOREIGN KEY (`" + newFKName + "`) ") + .append("REFERENCES `" + apptableName + "` (`id`)"); + } else { + sqlBuilder = sqlBuilder.append("ALTER TABLE `" + datatableName + "` ") + .append("DROP FOREIGN KEY `fk_" + oldConstraintName + "`,") + .append("CHANGE COLUMN `" + oldFKName + "` `" + newFKName + "` BIGINT(20) NOT NULL,") + .append("ADD CONSTRAINT `fk_" + newConstraintName + "` ").append("FOREIGN KEY (`" + newFKName + "`) ") + .append("REFERENCES `" + apptableName + "` (`id`)"); + } - StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`"); + this.jdbcTemplate.execute(sqlBuilder.toString()); - for (JsonElement column : dropColumns) { - parseDatatableColumnForDrop(column.getAsJsonObject(), sqlBuilder); - } + deregisterDatatable(datatableName); + registerDatatable(datatableName, apptableName); + } + } - // Remove the first comma, right after ALTER TABLE `datatable` - int indexOfFirstComma = sqlBuilder.indexOf(","); - if (indexOfFirstComma != -1) { - sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma); - } + if (changeColumns == null && addColumns == null && dropColumns == null) { return; } - this.jdbcTemplate.execute(sqlBuilder.toString()); - } - if (addColumns != null) { + if (dropColumns != null) { - StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`"); + StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`"); - for (JsonElement column : addColumns) { - parseDatatableColumnForAdd(column.getAsJsonObject(), sqlBuilder); - } + for (JsonElement column : dropColumns) { + parseDatatableColumnForDrop(column.getAsJsonObject(), sqlBuilder); + } - // Remove the first comma, right after ALTER TABLE `datatable` - int indexOfFirstComma = sqlBuilder.indexOf(","); - if (indexOfFirstComma != -1) { - sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma); - } - - this.jdbcTemplate.execute(sqlBuilder.toString()); - } - if (changeColumns != null) { + // Remove the first comma, right after ALTER TABLE `datatable` + int indexOfFirstComma = sqlBuilder.indexOf(","); + if (indexOfFirstComma != -1) { + sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma); + } - StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`"); + this.jdbcTemplate.execute(sqlBuilder.toString()); + } + if (addColumns != null) { - for (JsonElement column : changeColumns) { - parseDatatableColumnForUpdate(column.getAsJsonObject(), mapColumnNameDefinition, sqlBuilder); - } + StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`"); - // Remove the first comma, right after ALTER TABLE `datatable` - int indexOfFirstComma = sqlBuilder.indexOf(","); - if (indexOfFirstComma != -1) { - sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma); - } + for (JsonElement column : addColumns) { + parseDatatableColumnForAdd(column.getAsJsonObject(), sqlBuilder); + } - this.jdbcTemplate.execute(sqlBuilder.toString()); - } - } catch (SQLGrammarException e) { - Throwable realCause = e.getCause(); - List dataValidationErrors = new ArrayList(); - final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("datatable"); + // Remove the first comma, right after ALTER TABLE `datatable` + int indexOfFirstComma = sqlBuilder.indexOf(","); + if (indexOfFirstComma != -1) { + sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma); + } - if (realCause.getMessage().toLowerCase().contains("unknown column")) { - baseDataValidator.reset().parameter("name").failWithCode("does.not.exist"); - } else if (realCause.getMessage().toLowerCase().contains("can't drop")) { - baseDataValidator.reset().parameter("name").failWithCode("does.not.exist"); - } else if (realCause.getMessage().toLowerCase().contains("duplicate column")) { - baseDataValidator.reset().parameter("name").failWithCode("column.already.exists"); - } + this.jdbcTemplate.execute(sqlBuilder.toString()); + } + if (changeColumns != null) { - throwExceptionIfValidationWarningsExist(dataValidationErrors); - } + StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`"); + + for (JsonElement column : changeColumns) { + parseDatatableColumnForUpdate(column.getAsJsonObject(), mapColumnNameDefinition, sqlBuilder); + } + + // Remove the first comma, right after ALTER TABLE `datatable` + int indexOfFirstComma = sqlBuilder.indexOf(","); + if (indexOfFirstComma != -1) { + sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma); + } + + this.jdbcTemplate.execute(sqlBuilder.toString()); + } + } catch (SQLGrammarException e) { + Throwable realCause = e.getCause(); + List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("datatable"); + + if (realCause.getMessage().toLowerCase().contains("unknown column")) { + baseDataValidator.reset().parameter("name").failWithCode("does.not.exist"); + } else if (realCause.getMessage().toLowerCase().contains("can't drop")) { + baseDataValidator.reset().parameter("name").failWithCode("does.not.exist"); + } else if (realCause.getMessage().toLowerCase().contains("duplicate column")) { + baseDataValidator.reset().parameter("name").failWithCode("column.already.exists"); + } + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } } @Transactional @Override public void deleteDatatable(final String datatableName) { - try { - context.authenticatedUser(); + try { + context.authenticatedUser(); + if (!isRegisteredDataTable(datatableName)) { throw new DatatableNotFoundException(datatableName); } + validateDatatableName(datatableName); + deregisterDatatable(datatableName); + String sql = "DROP TABLE `" + datatableName + "`"; + this.jdbcTemplate.execute(sql); + } catch (SQLGrammarException e) { + Throwable realCause = e.getCause(); + List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("datatable"); + if (realCause.getMessage().contains("Unknown table")) { + baseDataValidator.reset().parameter("datatableName").failWithCode("does.not.exist"); + } - validateDatatableName(datatableName); - deregisterDatatable(datatableName); - - String sql = "DROP TABLE `" + datatableName + "`"; - this.jdbcTemplate.execute(sql); - } catch (SQLGrammarException e) { - Throwable realCause = e.getCause(); - List dataValidationErrors = new ArrayList(); - final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("datatable"); - - if (realCause.getMessage().contains("Unknown table")) { - baseDataValidator.reset().parameter("datatableName").failWithCode("does.not.exist"); - } - - throwExceptionIfValidationWarningsExist(dataValidationErrors); - } + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } } @Transactional From 4f83102834c90ee9ab5f8d536ee8a11d03f55ec9 Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Tue, 25 Jun 2013 14:58:44 +0200 Subject: [PATCH 14/16] update latest version info on readme so doesnt need to change each release --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e46a6b860..8d1ace46e 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ The mifos-platform (project named 'Mifos X') has reached public release versi That means there is a published and stable API. This API is documented in the api-docs under Full API Matrix. -Latest stable release: mifosplatform-1.4.0 +Latest stable release can always been viewed on master branch: Latest Release on Master, View change log License ============= From 678728c2b7ad15ebd4caa4a45b0dec0dfb675a43 Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Tue, 25 Jun 2013 15:25:43 +0200 Subject: [PATCH 15/16] udpate changelog 1.4.1 release --- CHANGELOG.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 029689247..df921bb58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ See VERSIONING (https://github.com/openMF/mifosx/blob/master/VERSIONING.md) for Releases =============== - +1. 25 Jun 2013 - 1.4.1.RELEASE 1. 20 Jun 2013 - 1.4.0.RELEASE 1. 10 Jun 2013 - 1.3.0.RELEASE 1. 06 Jun 2013 - 1.2.1.RELEASE @@ -25,6 +25,26 @@ Releases 1. 05 April 2013 - 0.11.1.beta 1. 05 April 2013 - 0.11.0.beta +1.4.1.RELEASE +============= +Bug Release + +Platform & API + +Bug + - [MIFOSX-362] - Date format is not similar In Balance Sheet as in other pages. + - [MIFOSX-473] - "Accounting Rules" created for Branch office (at Head office) is not displaying in head office. + - [MIFOSX-479] - Scheduled jobs only run for tenant with identifier 'default' + - [MIFOSX-480] - Not able to "Create A Group" under Branch Office using Head Office staff. + - [MIFOSX-488] - With DELETE datatables service, one can drop system tables + +Reference App + - [MIFOSX-388] - Buttons on table based reports no longer working + - [MIFOSX-463] - Numeric ids are number formated (causing NumberFormatException ) + - [MIFOSX-467] - Unable to create group in spanish locale as activation date doesnt pick up on culture/locale in datepicker + - [MIFOSX-481] - Not able to navigate to the immediate previous page from "Clients loan application" page + - [MIFOSX-482] - In Groups -> Clients loan application page -> no error message is displaying asking for proper inputs + - [MIFOSX-484] - While creating a "new client" for group (in branch office), Not able to select "Staff" from that (branch) office, if user is Admin. 1.4.0.RELEASE ============= From 5db999b5ba2b4e5c3157b9b50fab8c2a9aaa006e Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Tue, 25 Jun 2013 14:27:17 +0100 Subject: [PATCH 16/16] update release to 1.4.1 --- api-docs/apiLive.htm | 2 +- mifosng-provider/gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index ed0637439..18745eeb8 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -90,7 +90,7 @@
- +

Overview

diff --git a/mifosng-provider/gradle.properties b/mifosng-provider/gradle.properties index 63c3accc6..04a0c7868 100644 --- a/mifosng-provider/gradle.properties +++ b/mifosng-provider/gradle.properties @@ -1 +1 @@ -releaseVersion=1.4.1.DEVELOP \ No newline at end of file +releaseVersion=1.4.1.RELEASE \ No newline at end of file