diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/api/AdHocJsonInputParams.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/api/AdHocJsonInputParams.java index 0cb33844e..a797a0f98 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/api/AdHocJsonInputParams.java +++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/api/AdHocJsonInputParams.java @@ -25,7 +25,10 @@ import java.util.Set; * Enum of all parameters passed in while creating/updating a AdHocQuery ***/ public enum AdHocJsonInputParams { - ID("id"), NAME("name"),QUERY("query"),TABLENAME("tableName"),TABLEFIELD("tableFields"), ISACTIVE("isActive"),EMAIL("email"); + ID("id"), NAME("name"),QUERY("query"),TABLENAME("tableName"), TABLEFIELDS("tableFields"), ISACTIVE("isActive"), + REPORT_RUN_FREQUENCY("reportRunFrequency"), + REPORT_RUN_EVERY("reportRunEvery"), + EMAIL("email"); private final String value; diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/data/AdHocData.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/data/AdHocData.java index f0fd7a858..81cfc9199 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/data/AdHocData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/data/AdHocData.java @@ -18,13 +18,13 @@ */ package org.apache.fineract.adhocquery.data; -import java.util.Collection; - -import org.apache.fineract.organisation.office.data.OfficeData; -import org.apache.fineract.useradministration.data.AppUserData; -import org.apache.fineract.useradministration.data.RoleData; +import org.apache.fineract.adhocquery.domain.ReportRunFrequency; +import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.joda.time.DateTime; -import org.joda.time.LocalDate; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; /** * Immutable data object represent note or case information AdHocData @@ -32,8 +32,8 @@ import org.joda.time.LocalDate; */ public class AdHocData { - - + + @SuppressWarnings("unused") private final Long id; @SuppressWarnings("unused") @@ -59,13 +59,19 @@ public class AdHocData { @SuppressWarnings("unused") private final String createdBy; - - + private final List reportRunFrequencies; - public AdHocData(final Long id, final String name,final String query, final String tableName,final String tableFields, - final boolean isActive, final DateTime createdOn, final Long createdById,final Long updatedById, - final DateTime updatedOn,final String createdBy,final String email - ) { + private final Long reportRunFrequency; + + private final Long reportRunEvery; + + private final DateTime lastRun; + + public AdHocData(final Long id, final String name, final String query, final String tableName, final String tableFields, + final boolean isActive, final DateTime createdOn, final Long createdById, final Long updatedById, + final DateTime updatedOn, final String createdBy, final String email, + final List reportRunFrequencies, final Long reportRunFrequency, final Long reportRunEvery, + final DateTime lastRun) { this.id = id; this.name=name; this.query=query; @@ -78,9 +84,17 @@ public class AdHocData { this.updatedOn=updatedOn; this.createdBy=createdBy; this.email=email; + this.reportRunFrequencies = reportRunFrequencies; + this.reportRunFrequency = reportRunFrequency; + this.reportRunEvery = reportRunEvery; + this.lastRun = lastRun; } public static AdHocData template() { - AdHocData adHocData = new AdHocData(null,null,null,null,null,false,null,null,null,null,null,null); + List reportRunFrequencies = Arrays.stream(ReportRunFrequency.values()).map(rrf -> new EnumOptionData( + (long) rrf.getValue(), rrf.getCode(), rrf.getCode() + )).collect(Collectors.toList()); + + AdHocData adHocData = new AdHocData(null,null,null,null,null,false,null,null,null,null,null,null, reportRunFrequencies, null, null, null); return adHocData; } public Long getId() { @@ -119,4 +133,16 @@ public class AdHocData { public String getCreatedBy() { return this.createdBy; } + public List getReportRunFrequencies() { + return this.reportRunFrequencies; + } + public Long getReportRunFrequency() { + return this.reportRunFrequency; + } + public Long getReportRunEvery() { + return this.reportRunEvery; + } + public DateTime getLastRun() { + return this.lastRun; + } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/domain/AdHoc.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/domain/AdHoc.java index be315ba99..54f98f37d 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/domain/AdHoc.java +++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/domain/AdHoc.java @@ -53,17 +53,24 @@ public class AdHoc extends AbstractAuditableCustom { @Column(name = "email", length = 500) private String email; - - + + @Column(name = "report_run_frequency_code") + private Long reportRunFrequency; + + @Column(name = "report_run_every") + private Long reportRunEvery; + @Column(name = "IsActive", nullable = false) private boolean isActive = false; - private AdHoc(final String name, final String query,final String tableName,final String tableFields ,final String email,final boolean isActive) { + private AdHoc(final String name, final String query,final String tableName,final String tableFields ,final String email, final Long reportRunFrequency, final Long reportRunEvery, final boolean isActive) { this.name = StringUtils.defaultIfEmpty(name, null); this.query=StringUtils.defaultIfEmpty(query,null); this.tableName=StringUtils.defaultIfEmpty(tableName,null); this.tableFields=StringUtils.defaultIfEmpty(tableFields,null); this.email=StringUtils.defaultIfEmpty(email,null); + this.reportRunFrequency = reportRunFrequency; + this.reportRunEvery = reportRunEvery; this.isActive = BooleanUtils.toBooleanDefaultIfNull(isActive, false); } @@ -75,10 +82,12 @@ public class AdHoc extends AbstractAuditableCustom { SQLInjectionValidator.validateAdhocQuery(commandQuery); final String query = commandQuery; final String tableName = command.stringValueOfParameterNamed(AdHocJsonInputParams.TABLENAME.getValue()); - final String tableFields = command.stringValueOfParameterNamed(AdHocJsonInputParams.TABLEFIELD.getValue()); + final String tableFields = command.stringValueOfParameterNamed(AdHocJsonInputParams.TABLEFIELDS.getValue()); final String email = command.stringValueOfParameterNamed(AdHocJsonInputParams.EMAIL.getValue()); + final Long reportRunFrequency = command.longValueOfParameterNamed(AdHocJsonInputParams.REPORT_RUN_FREQUENCY.getValue()); + final Long reportRunEvery = command.longValueOfParameterNamed(AdHocJsonInputParams.REPORT_RUN_EVERY.getValue()); final boolean isActive = command.booleanPrimitiveValueOfParameterNamed(AdHocJsonInputParams.ISACTIVE.getValue()); - return new AdHoc(name,query,tableName,tableFields ,email,isActive); + return new AdHoc(name,query,tableName,tableFields, email, reportRunFrequency, reportRunEvery, isActive); } public Map update(final JsonCommand command) { @@ -104,7 +113,7 @@ public class AdHoc extends AbstractAuditableCustom { actualChanges.put(tableName, newValue); this.tableName = newValue; } - final String tableField = "tableField"; + final String tableField = "tableFields"; if (command.isChangeInStringParameterNamed(tableField, this.tableFields)) { final String newValue = command.stringValueOfParameterNamed(tableField); actualChanges.put(tableField, newValue); @@ -116,6 +125,18 @@ public class AdHoc extends AbstractAuditableCustom { actualChanges.put(email, newValue); this.email = newValue; } + final String reportRunFrequency = "reportRunFrequency"; + if (command.isChangeInLongParameterNamed(reportRunFrequency, this.getReportRunFrequency())) { + final Long newValue = command.longValueOfParameterNamed(reportRunFrequency); + actualChanges.put(reportRunFrequency, newValue); + this.reportRunFrequency = newValue; + } + final String reportRunEvery = "reportRunEvery"; + if (command.isChangeInLongParameterNamed(reportRunEvery, this.getReportRunEvery())) { + final Long newValue = command.longValueOfParameterNamed(reportRunEvery); + actualChanges.put(reportRunEvery, newValue); + this.reportRunEvery = newValue; + } final String paramisActive = "isActive"; if (command.isChangeInBooleanParameterNamed(paramisActive, this.isActive)) { final Boolean newValue = command.booleanObjectValueOfParameterNamed(paramisActive); @@ -149,5 +170,10 @@ public class AdHoc extends AbstractAuditableCustom { public void enableActive() { this.isActive = false; } - + public Long getReportRunFrequency() { + return this.reportRunFrequency; + } + public Long getReportRunEvery() { + return this.reportRunEvery; + } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/domain/ReportRunFrequency.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/domain/ReportRunFrequency.java new file mode 100644 index 000000000..fc02d4bc7 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/domain/ReportRunFrequency.java @@ -0,0 +1,55 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.adhocquery.domain; + +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +public enum ReportRunFrequency { + DAILY(1, "reportRunFrequency.daily"), + WEEKLY(2, "reportRunFrequency.weekly"), + MONTHLY(3, "reportRunFrequency.monthly"), + YEARLY(4, "reportRunFrequency.yearly"), + CUSTOM(5, "reportRunFrequency.custom"); + + private static final Map MAP = Arrays.stream(ReportRunFrequency.values()).collect(Collectors.toMap( + ReportRunFrequency::getValue, e -> e + )); + + private final long value; + private final String code; + + private ReportRunFrequency(final long value, final String code) { + this.value = value; + this.code = code; + } + + public long getValue() { + return this.value; + } + + public String getCode() { + return this.code; + } + + public static ReportRunFrequency fromId(final long id) { + return ReportRunFrequency.MAP.get(id); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocDataValidator.java index 2cdd294a9..9cb13e8ce 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocDataValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocDataValidator.java @@ -27,6 +27,7 @@ import java.util.Map; import java.util.Set; import org.apache.commons.lang.StringUtils; +import org.apache.fineract.adhocquery.domain.ReportRunFrequency; import org.apache.fineract.infrastructure.core.data.ApiParameterError; import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; import org.apache.fineract.infrastructure.core.exception.InvalidJsonException; @@ -44,7 +45,9 @@ public final class AdHocDataValidator { /** * The parameters supported for this command. */ - private final Set supportedParameters = new HashSet<>(Arrays.asList("name","query","tableName","tableFields","email","isActive")); + private final Set supportedParameters = new HashSet<>(Arrays.asList( + "name","query","tableName","tableFields","email","isActive", "reportRunFrequency", "reportRunEvery" + )); private final FromJsonHelper fromApiJsonHelper; @@ -77,7 +80,19 @@ public final class AdHocDataValidator { baseDataValidator.reset().parameter("tableFields").value(tableFields).notBlank().notExceedingLengthOf(1000); final String email = this.fromApiJsonHelper.extractStringNamed("email", element); - baseDataValidator.reset().parameter("email").value(email).notBlank().notExceedingLengthOf(500); + baseDataValidator.reset().parameter("email").value(email).ignoreIfNull().notExceedingLengthOf(500); + + final Long reportRunFrequencyCode = this.fromApiJsonHelper.extractLongNamed("reportRunFrequency", element); + if (reportRunFrequencyCode != null) { + baseDataValidator.reset().parameter("reportRunFrequency").value(reportRunFrequencyCode) + .inMinMaxRange((int) ReportRunFrequency.DAILY.getValue(), (int) ReportRunFrequency.CUSTOM.getValue()); + } + + final Long reportRunEvery = this.fromApiJsonHelper.extractLongNamed("reportRunEvery", element); + if (reportRunEvery != null) { + baseDataValidator.reset().parameter("reportRunEvery").value(reportRunFrequencyCode).integerGreaterThanZero(); + } + throwExceptionIfValidationWarningsExist(dataValidationErrors); } @@ -111,7 +126,16 @@ public final class AdHocDataValidator { } if (this.fromApiJsonHelper.parameterExists("email", element)) { final String email = this.fromApiJsonHelper.extractStringNamed("email", element); - baseDataValidator.reset().parameter("email").value(email).notBlank().notExceedingLengthOf(500); + baseDataValidator.reset().parameter("email").value(email).ignoreIfNull().notExceedingLengthOf(500); + } + if (this.fromApiJsonHelper.parameterExists("reportRunFrequency", element)) { + final Long reportRunFrequencyCode = this.fromApiJsonHelper.extractLongNamed("reportRunFrequency", element); + baseDataValidator.reset().parameter("reportRunFrequency").value(reportRunFrequencyCode) + .inMinMaxRange((int) ReportRunFrequency.DAILY.getValue(), (int) ReportRunFrequency.CUSTOM.getValue()); + } + if (this.fromApiJsonHelper.parameterExists("reportRunEvery", element)) { + final Long reportRunEvery = this.fromApiJsonHelper.extractLongNamed("reportRunEvery", element); + baseDataValidator.reset().parameter("reportRunEvery").value(reportRunEvery).integerGreaterThanZero(); } /*if (this.fromApiJsonHelper.parameterExists("isActive", element)) { final Integer isActive = this.fromApiJsonHelper.extractIntegerNamed("isActive", element, Locale.getDefault()); diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocReadPlatformServiceImpl.java index eb1f6335e..c5c7a0af6 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocReadPlatformServiceImpl.java @@ -88,13 +88,17 @@ public class AdHocReadPlatformServiceImpl implements AdHocReadPlatformService { final DateTime updatedOn=JdbcSupport.getDateTime(rs, "updatedOn"); final String createdByUsername=rs.getString("createdBy"); final String email=rs.getString("email"); - - return new AdHocData(id,name,query, tableName,tableFields,isActive,createdDate,createdById,updatedById,updatedOn,createdByUsername,email); + final Long reportRunFrequency=JdbcSupport.getLong(rs, "report_run_frequency_code"); + final Long reportRunEvery=JdbcSupport.getLong(rs, "report_run_every"); + final DateTime lastRun = JdbcSupport.getDateTime(rs, "last_run"); + + return new AdHocData(id,name,query, tableName,tableFields,isActive,createdDate,createdById,updatedById,updatedOn,createdByUsername,email, AdHocData.template().getReportRunFrequencies(), reportRunFrequency, reportRunEvery, lastRun); } public String schema() { return " r.id as id, r.name as name, r.query as query, r.table_name as tableName,r.table_fields as tableField ,r.IsActive as isActive ,r.email as email ," - +" r.created_date as createdDate, r.createdby_id as createdById,cb.username as createdBy,r.lastmodifiedby_id as updatedById ,r.lastmodified_date as updatedOn " + + " r.report_run_frequency_code, r.report_run_every, r.last_run, " + + " r.created_date as createdDate, r.createdby_id as createdById,cb.username as createdBy,r.lastmodifiedby_id as updatedById ,r.lastmodified_date as updatedOn " + " from m_adhoc r left join m_appuser cb on cb.id=r.createdby_id left join m_appuser mb on mb.id=r.lastmodifiedby_id"; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocScheduledJobRunnerServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocScheduledJobRunnerServiceImpl.java index d10cdddec..c92121c98 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocScheduledJobRunnerServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocScheduledJobRunnerServiceImpl.java @@ -18,13 +18,17 @@ */ package org.apache.fineract.adhocquery.service; -import java.util.Collection; - import org.apache.fineract.adhocquery.data.AdHocData; +import org.apache.fineract.adhocquery.domain.ReportRunFrequency; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import org.apache.fineract.infrastructure.jobs.annotation.CronTarget; import org.apache.fineract.infrastructure.jobs.service.JobName; +import org.joda.time.DateTime; +import org.joda.time.Days; +import org.joda.time.LocalDate; +import org.joda.time.Months; +import org.joda.time.Years; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -32,6 +36,9 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.Collection; +import java.util.Date; + @Service(value = "adHocScheduledJobRunnerService") public class AdHocScheduledJobRunnerServiceImpl implements AdHocScheduledJobRunnerService { @@ -47,6 +54,7 @@ public class AdHocScheduledJobRunnerServiceImpl implements AdHocScheduledJobRunn this.adHocReadPlatformService = adHocReadPlatformService; } + @Transactional @Override @CronTarget(jobName = JobName.GENERATE_ADHOCCLIENT_SCEHDULE) @@ -54,16 +62,56 @@ public class AdHocScheduledJobRunnerServiceImpl implements AdHocScheduledJobRunn final Collection adhocs = this.adHocReadPlatformService.retrieveAllActiveAdHocQuery(); if(adhocs.size()>0){ adhocs.forEach(adhoc->{ - //jdbcTemplate.execute("truncate table "+adhoc.getTableName()); - final StringBuilder insertSqlBuilder = new StringBuilder(900); - insertSqlBuilder - .append("INSERT INTO ") - .append(adhoc.getTableName()+"(") - .append(adhoc.getTableFields()+") ") - .append(adhoc.getQuery()); - if (insertSqlBuilder.length() > 0) { - final int result = this.jdbcTemplate.update(insertSqlBuilder.toString()); - logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Results affected by inserted: " + result); + boolean run = true; + LocalDate next = null; + if (adhoc.getReportRunFrequency() != null) { + if (adhoc.getLastRun() != null) { + LocalDate start = adhoc.getLastRun().toLocalDate(); + LocalDate end = new DateTime().toLocalDate(); + switch (ReportRunFrequency.fromId(adhoc.getReportRunFrequency())) { + case DAILY: + next = start.plusDays(1); + run = Days.daysBetween(start, end).getDays() >= 1; + break; + case WEEKLY: + next = start.plusDays(7); + run = Days.daysBetween(start, end).getDays() >= 7; + break; + case MONTHLY: + next = start.plusMonths(1); + run = Months.monthsBetween(start, end).getMonths() >= 1; + break; + case YEARLY: + next = start.plusYears(1); + run = Years.yearsBetween(start, end).getYears() >= 1; + break; + case CUSTOM: + next = start.plusDays((int) (long) adhoc.getReportRunEvery()); + run = Days.daysBetween(start, end).getDays() >= adhoc.getReportRunEvery(); + break; + default: + throw new IllegalStateException(); + } + + } + } + + if (run) { + //jdbcTemplate.execute("truncate table "+adhoc.getTableName()); + final StringBuilder insertSqlBuilder = new StringBuilder(900); + insertSqlBuilder + .append("INSERT INTO ") + .append(adhoc.getTableName()+"(") + .append(adhoc.getTableFields()+") ") + .append(adhoc.getQuery()); + if (insertSqlBuilder.length() > 0) { + final int result = this.jdbcTemplate.update(insertSqlBuilder.toString()); + logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Results affected by inserted: " + result); + + this.jdbcTemplate.update("UPDATE m_adhoc SET last_run=? WHERE id=?", new Date(), adhoc.getId()); + } + } else { + logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Skipping execution of " + adhoc.getName() + ", scheduled for execution on " + next); } }); }else{ diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/utils/SQLInjectionValidator.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/utils/SQLInjectionValidator.java index 60c207009..d03b2f460 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/utils/SQLInjectionValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/utils/SQLInjectionValidator.java @@ -30,7 +30,7 @@ public class SQLInjectionValidator { private final static String[] COMMENTS = { "--", "({", "/*", "#" }; - private final static String SQL_PATTERN = "[a-zA-Z_=,\\-'!><.?\"`% ()0-9]*"; + private final static String SQL_PATTERN = "[a-zA-Z_=,\\-'!><.?\"`% ()0-9*\n\r]*"; public final static void validateSQLInput(final String sqlSearch) { String lowerCaseSQL = sqlSearch.toLowerCase(); @@ -115,9 +115,9 @@ public class SQLInjectionValidator { } } public final static void validateAdhocQuery(final String sqlSearch) { - String lowerCaseSQL = sqlSearch.toLowerCase(); + String lowerCaseSQL = sqlSearch.toLowerCase().trim(); for (String ddl : DDL_COMMANDS) { - if (lowerCaseSQL.contains(ddl)) { + if (lowerCaseSQL.startsWith(ddl)) { throw new SQLInjectionException(); } } diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V336__report-run-frequency.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V336__report-run-frequency.sql new file mode 100644 index 000000000..3f593c41e --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V336__report-run-frequency.sql @@ -0,0 +1,22 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. +-- + +ALTER TABLE m_adhoc ADD COLUMN report_run_frequency_code INT NULL DEFAULT NULL; +ALTER TABLE m_adhoc ADD COLUMN report_run_every INT NULL DEFAULT NULL; +ALTER TABLE m_adhoc ADD COLUMN last_run TIMESTAMP NULL DEFAULT NULL; \ No newline at end of file diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V337__nullable-adhoc-email.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V337__nullable-adhoc-email.sql new file mode 100644 index 000000000..0f7296fb0 --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V337__nullable-adhoc-email.sql @@ -0,0 +1,20 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. +-- + +ALTER TABLE m_adhoc MODIFY `email` VARCHAR(500) NULL DEFAULT NULL; \ No newline at end of file