From 28b23fd313708af5d710522593042a1e127790d7 Mon Sep 17 00:00:00 2001 From: Bernd Bestel Date: Fri, 4 Oct 2019 11:14:11 +0200 Subject: [PATCH] Implemented chore period intervals to have more flexible schedules (closes #312) --- changelog/54_UNRELEASED_2019-xx-xx.md | 3 ++ localization/strings.pot | 12 +++++++ migrations/0094.sql | 47 +++++++++++++++++++++++++++ public/viewjs/choreform.js | 4 +++ views/choreform.blade.php | 12 +++++++ 5 files changed, 78 insertions(+) create mode 100644 migrations/0094.sql diff --git a/changelog/54_UNRELEASED_2019-xx-xx.md b/changelog/54_UNRELEASED_2019-xx-xx.md index 4a0474d7..ac047edd 100644 --- a/changelog/54_UNRELEASED_2019-xx-xx.md +++ b/changelog/54_UNRELEASED_2019-xx-xx.md @@ -4,5 +4,8 @@ ### Recipe fixes - Fixed that recipes were displayed without ingredients if the total recipe count was > 100 +### Chores improvements +- Added a "period interval" option per chore to have more flexible schedules (possible for the daily/weekly/monthly schedules, means "schedule this chore only every x days/weeks/months" to have for example biweekly schedules) + ### General & other improvements - New Input shorthands for date fields to increase/decrease the date by 1 month/year (shift + arrow keys, see the full list [here](https://github.com/grocy/grocy#input-shorthands-for-date-fields)) diff --git a/localization/strings.pot b/localization/strings.pot index 08be8873..af719145 100644 --- a/localization/strings.pot +++ b/localization/strings.pot @@ -1546,3 +1546,15 @@ msgstr "" msgid "Are you sure to remove the included recipe \"%s\"?" msgstr "" + +msgid "Period interval" +msgstr "" + +msgid "This means the next execution of this chore should only be scheduled every %s days" +msgstr "" + +msgid "This means the next execution of this chore should only be scheduled every %s weeks" +msgstr "" + +msgid "This means the next execution of this chore should only be scheduled every %s months" +msgstr "" diff --git a/migrations/0094.sql b/migrations/0094.sql new file mode 100644 index 00000000..bba7340e --- /dev/null +++ b/migrations/0094.sql @@ -0,0 +1,47 @@ +ALTER TABLE chores +ADD period_interval INTEGER NOT NULL DEFAULT 1 CHECK(period_interval > 0); + +DROP VIEW chores_current; +CREATE VIEW chores_current +AS +SELECT + x.chore_id, + x.last_tracked_time, + CASE WHEN x.rollover = 1 AND DATETIME('now', 'localtime') > x.next_estimated_execution_time THEN + DATETIME(STRFTIME('%Y-%m-%d', DATETIME('now', 'localtime')) || ' ' || STRFTIME('%H:%M:%S', x.next_estimated_execution_time)) + ELSE + x.next_estimated_execution_time + END AS next_estimated_execution_time, + x.track_date_only, + x.next_execution_assigned_to_user_id +FROM ( + +SELECT + h.id AS chore_id, + MAX(l.tracked_time) AS last_tracked_time, + CASE h.period_type + WHEN 'manually' THEN '2999-12-31 23:59:59' + WHEN 'dynamic-regular' THEN DATETIME(MAX(l.tracked_time), '+' || CAST(h.period_days AS TEXT) || ' day') + WHEN 'daily' THEN DATETIME(IFNULL(MAX(l.tracked_time), DATETIME('now', 'localtime')), '+' || CAST(h.period_interval AS TEXT) || ' day') + WHEN 'weekly' THEN + CASE + WHEN period_config LIKE '%sunday%' THEN DATETIME(IFNULL(MAX(l.tracked_time), DATETIME('now', 'localtime')), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 0') + WHEN period_config LIKE '%monday%' THEN DATETIME(IFNULL(MAX(l.tracked_time), DATETIME('now', 'localtime')), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 1') + WHEN period_config LIKE '%tuesday%' THEN DATETIME(IFNULL(MAX(l.tracked_time), DATETIME('now', 'localtime')), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 2') + WHEN period_config LIKE '%wednesday%' THEN DATETIME(IFNULL(MAX(l.tracked_time), DATETIME('now', 'localtime')), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 3') + WHEN period_config LIKE '%thursday%' THEN DATETIME(IFNULL(MAX(l.tracked_time), DATETIME('now', 'localtime')), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 4') + WHEN period_config LIKE '%friday%' THEN DATETIME(IFNULL(MAX(l.tracked_time), DATETIME('now', 'localtime')), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 5') + WHEN period_config LIKE '%saturday%' THEN DATETIME(IFNULL(MAX(l.tracked_time), DATETIME('now', 'localtime')), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 6') + END + WHEN 'monthly' THEN DATETIME(IFNULL(MAX(l.tracked_time), DATETIME('now', 'localtime')), '+' || CAST(h.period_interval AS TEXT) || ' month', 'start of month', '+' || CAST(h.period_days - 1 AS TEXT) || ' day') + END AS next_estimated_execution_time, + h.track_date_only, + h.rollover, + h.next_execution_assigned_to_user_id +FROM chores h +LEFT JOIN chores_log l + ON h.id = l.chore_id + AND l.undone = 0 +GROUP BY h.id, h.period_days + +) x; diff --git a/public/viewjs/choreform.js b/public/viewjs/choreform.js index 1bd77713..ee577e22 100644 --- a/public/viewjs/choreform.js +++ b/public/viewjs/choreform.js @@ -118,6 +118,7 @@ $('.input-group-chore-period-type').on('change', function(e) { var periodType = $('#period_type').val(); var periodDays = $('#period_days').val(); + var periodInterval = $('#period_interval').val(); $(".period-type-input").addClass("d-none"); $(".period-type-" + periodType).removeClass("d-none"); @@ -139,11 +140,13 @@ $('.input-group-chore-period-type').on('change', function(e) else if (periodType === 'daily') { $('#chore-period-type-info').text(__t('This means the next execution of this chore is scheduled 1 day after the last execution')); + $('#chore-period-interval-info').text(__t('This means the next execution of this chore should only be scheduled every %s days', periodInterval.toString())); } else if (periodType === 'weekly') { $('#chore-period-type-info').text(__t('This means the next execution of this chore is scheduled 1 day after the last execution, but only for the weekdays selected below')); $("#period_config").val($(".period-type-weekly input:checkbox:checked").map(function () { return this.value; }).get().join(",")); + $('#chore-period-interval-info').text(__t('This means the next execution of this chore should only be scheduled every %s weeks', periodInterval.toString())); } else if (periodType === 'monthly') { @@ -152,6 +155,7 @@ $('.input-group-chore-period-type').on('change', function(e) $("#period_days").attr("min", "1"); $("#period_days").attr("max", "31"); $("#period_days").parent().find(".invalid-feedback").text(__t('The amount must be between %1$s and %2$s', "1", "31")); + $('#chore-period-interval-info').text(__t('This means the next execution of this chore should only be scheduled every %s months', periodInterval.toString())); } Grocy.FrontendHelpers.ValidateForm('chore-form'); diff --git a/views/choreform.blade.php b/views/choreform.blade.php index 0287b65e..f4ca391e 100644 --- a/views/choreform.blade.php +++ b/views/choreform.blade.php @@ -86,6 +86,18 @@ + @php if($mode == 'edit') { $value = $chore->period_interval; } else { $value = 1; } @endphp + @include('components.numberpicker', array( + 'id' => 'period_interval', + 'label' => 'Period interval', + 'value' => $value, + 'min' => '1', + 'additionalCssClasses' => 'input-group-chore-period-type', + 'invalidFeedback' => $__t('This cannot be lower than %s', '1'), + 'additionalGroupCssClasses' => 'period-type-input period-type-daily period-type-weekly period-type-monthly', + 'hintId' => 'chore-period-interval-info' + )) + @if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)