[Bps-public-commit] rt-extension-repeatticket branch add-mode-create-ticket-on-recurring-date created. 2.00-12-g49096bf

BPS Git Server git at git.bestpractical.com
Mon Aug 28 21:08:47 UTC 2023


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "rt-extension-repeatticket".

The branch, add-mode-create-ticket-on-recurring-date has been created
        at  49096bf7da3701e81b0c94401a975ad2bc994801 (commit)

- Log -----------------------------------------------------------------
commit 49096bf7da3701e81b0c94401a975ad2bc994801
Author: Brad Embree <brad at bestpractical.com>
Date:   Mon Aug 28 14:02:27 2023 -0700

    Add recurrence preview
    
    Show a list of the next X tickets that will be scheduled based on the
    recurrence config

diff --git a/html/Ticket/Elements/EditRecurrence b/html/Ticket/Elements/EditRecurrence
index fbfb751..bfddc4d 100644
--- a/html/Ticket/Elements/EditRecurrence
+++ b/html/Ticket/Elements/EditRecurrence
@@ -255,6 +255,35 @@ jQuery( function () {
       </div>
     </div>
   </div>
+
+  <div class="<% $ARGSRef->{'repeat-enabled'} && $ReadOnly && $preview_num ? '' : 'hidden' %>" id="tickets-preview" style="text-align:center;">
+    <&| /Widgets/TitleBox, title => loc("Recurrence Preview") &>
+      <div class="form-row" style="text-align:left;">
+        <p>This shows the next <% $preview_num %> tickets that would be created with the current recurrence configuration.</p>
+
+        <p>If the recurrence configuration uses the concurrent active tickets option or the create new task after task is complete option the preview may not be accurate as it depends on whether previously created tickets are still active.</p>
+      </div>
+      <div class="form-row">
+        <div class="col-6">
+          <span><% loc('Starts') %></span>
+        </div>
+        <div class="col-6">
+          <span><% loc('Due') %></span>
+        </div>
+      </div>
+% foreach my $preview ( @preview_tickets ) {
+      <div class="form-row">
+        <div class="col-6">
+          <span><% $preview->[0] %></span>
+        </div>
+        <div class="col-6">
+          <span><% $preview->[1] %></span>
+        </div>
+      </div>
+% }
+    </&>
+    </div>
+
 </div>
 
 <%init>
@@ -263,8 +292,9 @@ my @week_values = qw(su mo tu we th fr sa);
 my @week_number_labels = qw(First Second Third Fourth Last); # loc_qw
 my @month_labels = qw(January February March April  May June July August September October November December); # loc_qw
 
+my $repeat;
 if ( $Ticket ) {
-    my ($repeat) = $Ticket->Attributes->Named('RepeatTicketSettings');
+    ($repeat) = $Ticket->Attributes->Named('RepeatTicketSettings');
     if ( $repeat ) {
         $ARGSRef = $repeat->Content if $repeat;
     }
@@ -341,6 +371,35 @@ my $month = sub {
     $str .= "</select>";
     return $str;
 };
+
+my @preview_tickets;
+my $preview_num = RT->Config->Get('RepeatTicketPreviewNumber') // 5;
+unless ( $Initial ) {
+  # find next X tickets to be created for preview window
+  if ( $preview_num ) {
+    RT::Extension::RepeatTicket::repeat_ticket_preview(1);
+
+    my $content = $repeat->Content;
+
+    my $set = RT::Extension::RepeatTicket::_build_set( $content, DateTime->today( time_zone => RT->Config->Get('Timezone') ) );
+    if ($set) {
+        # use a counter to ensure we don't get an endless loop trying to find the next X tickets
+        my $counter = 0;
+        my $iter = $set->iterator;
+        while ( my $dt = $iter->next ) {
+            if ( ! $content->{'repeat-create-on-recurring-date'} && $content->{'repeat-lead-time'} ) {
+                $dt = $dt->add( days => '-' . $content->{'repeat-lead-time'} );
+            }
+            push @preview_tickets, RT::Extension::RepeatTicket::Repeat( $repeat, $dt );
+            $counter++;
+
+            last if scalar @preview_tickets >= $preview_num || $counter > $preview_num * 2;
+        }
+    }
+
+    RT::Extension::RepeatTicket::repeat_ticket_preview(0);
+  }
+}
 </%init>
 <%args>
 $ARGSRef => undef

commit 9b4004143ad7e198e201a86c731b4d89b0b4d7bf
Author: Brad Embree <brad at bestpractical.com>
Date:   Mon Aug 28 13:54:28 2023 -0700

    Factor out code building date set into a method
    
    The ticket preview in the RT UI needs to use the same logic to build a
    date set that existed in the MaybeRepeatMore method. Factored out the
    logic into a method so both places can use it.

diff --git a/lib/RT/Extension/RepeatTicket.pm b/lib/RT/Extension/RepeatTicket.pm
index 4984596..b7faa86 100644
--- a/lib/RT/Extension/RepeatTicket.pm
+++ b/lib/RT/Extension/RepeatTicket.pm
@@ -674,92 +674,7 @@ sub MaybeRepeatMore {
 
     my @ids;
     if ( $tickets_needed ) {
-        my $set;
-        if ( $content->{'repeat-type'} eq 'daily' ) {
-            if ( $content->{'repeat-details-daily'} eq 'day' ) {
-                $set = DateTime::Event::ICal->recur(
-                    dtstart  => $last_due || $last_created,
-                    freq     => 'daily',
-                    interval => $content->{'repeat-details-daily-day'} || 1,
-                );
-            }
-            elsif ( $content->{'repeat-details-daily'} eq 'weekday' ) {
-                $set = DateTime::Event::ICal->recur(
-                    dtstart  => $last_due || $last_created,
-                    freq    => 'daily',
-                    byday   => [ 'mo', 'tu', 'we', 'th', 'fr' ],
-                );
-            }
-        }
-        elsif ( $content->{'repeat-type'} eq 'weekly' ) {
-            if ( $content->{'repeat-details-weekly'} eq 'week' ) {
-                my $weeks = $content->{'repeat-details-weekly-weeks'};
-                if ( defined $weeks ) {
-                    $set = DateTime::Event::ICal->recur(
-                        dtstart  => $last_due || $last_created,
-                        freq     => 'weekly',
-                        interval => $content->{'repeat-details-weekly-week'}
-                          || 1,
-                        byday => ref $weeks ? $weeks : [$weeks],
-                    );
-                }
-                else {
-                    $RT::Logger->error('No weeks defined');
-                }
-            }
-        }
-        elsif ( $content->{'repeat-type'} eq 'monthly' ) {
-            if ( $content->{'repeat-details-monthly'} eq 'day' ) {
-                $set = DateTime::Event::ICal->recur(
-                    dtstart  => $last_due || $last_created,
-                    freq     => 'monthly',
-                    interval => $content->{'repeat-details-monthly-day-month'}
-                      || 1,
-                    bymonthday => $content->{'repeat-details-monthly-day-day'}
-                      || 1,
-                );
-            }
-            elsif ( $content->{'repeat-details-monthly'} eq 'week' ) {
-                my $number = $content->{'repeat-details-monthly-week-number'}
-                  || 1;
-                my $day = $content->{'repeat-details-monthly-week-week'}
-                  || 'mo';
-
-                $set = DateTime::Event::ICal->recur(
-                    dtstart  => $last_due || $last_created,
-                    freq     => 'monthly',
-                    interval => $content->{'repeat-details-monthly-week-month'}
-                      || 1,
-                    byday => $number . $day,
-                );
-            }
-        }
-        elsif ( $content->{'repeat-type'} eq 'yearly' ) {
-            if ( $content->{'repeat-details-yearly'} eq 'day' ) {
-                $set = DateTime::Event::ICal->recur(
-                    dtstart  => $last_due || $last_created,
-                    freq    => 'yearly',
-                    bymonth => $content->{'repeat-details-yearly-day-month'}
-                      || 1,
-                    bymonthday => $content->{'repeat-details-yearly-day-day'}
-                      || 1,
-                );
-            }
-            elsif ( $content->{'repeat-details-yearly'} eq 'week' ) {
-                my $number = $content->{'repeat-details-yearly-week-number'}
-                  || 1;
-                my $day = $content->{'repeat-details-yearly-week-week'} || 'mo';
-
-                $set = DateTime::Event::ICal->recur(
-                    dtstart  => $last_due || $last_created,
-                    freq    => 'yearly',
-                    bymonth => $content->{'repeat-details-yearly-week-month'}
-                      || 1,
-                    byday => $number . $day,
-                );
-            }
-        }
-
+        my $set = _build_set( $content, $last_due, $last_created );
         if ($set) {
             my @dates;
             my $iter = $set->iterator;
@@ -778,6 +693,98 @@ sub MaybeRepeatMore {
     return @ids;
 }
 
+sub _build_set {
+    my ( $content, $last_due, $last_created ) = @_;
+
+    my $set;
+    if ( $content->{'repeat-type'} eq 'daily' ) {
+        if ( $content->{'repeat-details-daily'} eq 'day' ) {
+            $set = DateTime::Event::ICal->recur(
+                dtstart  => $last_due || $last_created,
+                freq     => 'daily',
+                interval => $content->{'repeat-details-daily-day'} || 1,
+            );
+        }
+        elsif ( $content->{'repeat-details-daily'} eq 'weekday' ) {
+            $set = DateTime::Event::ICal->recur(
+                dtstart  => $last_due || $last_created,
+                freq    => 'daily',
+                byday   => [ 'mo', 'tu', 'we', 'th', 'fr' ],
+            );
+        }
+    }
+    elsif ( $content->{'repeat-type'} eq 'weekly' ) {
+        if ( $content->{'repeat-details-weekly'} eq 'week' ) {
+            my $weeks = $content->{'repeat-details-weekly-weeks'};
+            if ( defined $weeks ) {
+                $set = DateTime::Event::ICal->recur(
+                    dtstart  => $last_due || $last_created,
+                    freq     => 'weekly',
+                    interval => $content->{'repeat-details-weekly-week'}
+                        || 1,
+                    byday => ref $weeks ? $weeks : [$weeks],
+                );
+            }
+            else {
+                $RT::Logger->error('No weeks defined');
+            }
+        }
+    }
+    elsif ( $content->{'repeat-type'} eq 'monthly' ) {
+        if ( $content->{'repeat-details-monthly'} eq 'day' ) {
+            $set = DateTime::Event::ICal->recur(
+                dtstart  => $last_due || $last_created,
+                freq     => 'monthly',
+                interval => $content->{'repeat-details-monthly-day-month'}
+                    || 1,
+                bymonthday => $content->{'repeat-details-monthly-day-day'}
+                    || 1,
+            );
+        }
+        elsif ( $content->{'repeat-details-monthly'} eq 'week' ) {
+            my $number = $content->{'repeat-details-monthly-week-number'}
+                || 1;
+            my $day = $content->{'repeat-details-monthly-week-week'}
+                || 'mo';
+
+            $set = DateTime::Event::ICal->recur(
+                dtstart  => $last_due || $last_created,
+                freq     => 'monthly',
+                interval => $content->{'repeat-details-monthly-week-month'}
+                    || 1,
+                byday => $number . $day,
+            );
+        }
+    }
+    elsif ( $content->{'repeat-type'} eq 'yearly' ) {
+        if ( $content->{'repeat-details-yearly'} eq 'day' ) {
+            $set = DateTime::Event::ICal->recur(
+                dtstart  => $last_due || $last_created,
+                freq    => 'yearly',
+                bymonth => $content->{'repeat-details-yearly-day-month'}
+                    || 1,
+                bymonthday => $content->{'repeat-details-yearly-day-day'}
+                    || 1,
+            );
+        }
+        elsif ( $content->{'repeat-details-yearly'} eq 'week' ) {
+            my $number = $content->{'repeat-details-yearly-week-number'}
+                || 1;
+            my $day = $content->{'repeat-details-yearly-week-week'} || 'mo';
+
+            $set = DateTime::Event::ICal->recur(
+                dtstart  => $last_due || $last_created,
+                freq    => 'yearly',
+                bymonth => $content->{'repeat-details-yearly-week-month'}
+                    || 1,
+                byday => $number . $day,
+            );
+        }
+    }
+
+    return $set;
+}
+
 sub CheckCompleteStatus {
     my $ticket = shift;
     my $lifecycle =

commit 67526b5865658c0ff313389bd23390bc8ace8f78
Author: Brad Embree <brad at bestpractical.com>
Date:   Mon Aug 28 13:52:24 2023 -0700

    Add repeat ticket preview mode
    
    Add a preview mode to the repeat ticket logic to use in the RT UI.
    
    If preview mode is set then the Create method returns an array of start
    and due dates for each ticket that would have been created instead of an
    array of ticket ids.

diff --git a/lib/RT/Extension/RepeatTicket.pm b/lib/RT/Extension/RepeatTicket.pm
index d458abe..4984596 100644
--- a/lib/RT/Extension/RepeatTicket.pm
+++ b/lib/RT/Extension/RepeatTicket.pm
@@ -163,6 +163,16 @@ sub Run {
     return @ids;
 }
 
+my $repeat_ticket_preview = 0;
+sub repeat_ticket_preview {
+    my $val = shift;
+
+    $repeat_ticket_preview = $val
+        if defined $val;
+
+    return $repeat_ticket_preview;
+}
+
 sub Repeat {
     my $attr      = shift;
     my @checkdays = @_;
@@ -446,28 +456,34 @@ sub Repeat {
             }
         }
 
-        my ( $id, $txn, $msg ) = _RepeatTicket(
-            $repeat_ticket,
-            Starts => $starts->ISO,
-            $due
-            ? ( Due => $due->ISO )
-            : (),
-            Created => $starts->ISO,
-        );
-
-        if ($id) {
-            $RT::Logger->info(
-                "Repeated ticket " . $repeat_ticket->id . ": $id" );
-            $content->{'repeat-occurrences'}++;
-            $content->{'last-ticket'} = $id;
-            push @{ $content->{'tickets'} }, $id;
-            push @ids, $id;
+        if ( repeat_ticket_preview ) {
+            # return the ticket starts and due date
+            push @ids, [ $starts->Date, $due ? $due->Date : () ];
         }
         else {
-            $RT::Logger->error( "Failed to repeat ticket for "
-                  . $repeat_ticket->id
-                  . ": $msg" );
-            next;
+            my ( $id, $txn, $msg ) = _RepeatTicket(
+                $repeat_ticket,
+                Starts => $starts->ISO,
+                $due
+                ? ( Due => $due->ISO )
+                : (),
+                Created => $starts->ISO,
+            );
+
+            if ($id) {
+                $RT::Logger->info(
+                    "Repeated ticket " . $repeat_ticket->id . ": $id" );
+                $content->{'repeat-occurrences'}++;
+                $content->{'last-ticket'} = $id;
+                push @{ $content->{'tickets'} }, $id;
+                push @ids, $id;
+            }
+            else {
+                $RT::Logger->error( "Failed to repeat ticket for "
+                    . $repeat_ticket->id
+                    . ": $msg" );
+                next;
+            }
         }
     }
 

commit 63d4610b4e9ee7bfaa37a46a36552d70080af5f8
Author: Brad Embree <brad at bestpractical.com>
Date:   Sun Aug 20 19:56:17 2023 -0700

    Add tests
    
    Add new test file that tests simple mode

diff --git a/xt/create_on_recurring_date.t b/xt/create_on_recurring_date.t
new file mode 100644
index 0000000..e18206a
--- /dev/null
+++ b/xt/create_on_recurring_date.t
@@ -0,0 +1,113 @@
+use strict;
+use warnings;
+
+use RT::Extension::RepeatTicket::Test tests => undef;
+
+use_ok('RT::Extension::RepeatTicket');
+require_ok('bin/rt-repeat-ticket');
+
+my ( $baseurl, $m ) = RT::Test->started_ok();
+{
+    diag "Run with repeat-create-on-recurring-date value of 1 so repeat-coexistent-number is 0";
+    my $daily_id = run_tests($baseurl, $m);
+
+    # A new ticket should be created for any day recurrence script runs for after start date of today
+    my $today = DateTime->now;
+    my $next_id = $daily_id + 1;
+    ok(!(RT::Repeat::Ticket::Run->run()), 'Ran recurrence script for today.');
+    my $ticket = RT::Ticket->new(RT->SystemUser);
+    ok(!($ticket->Load($next_id)), "No ticket created for today");
+
+    my $tomorrow = DateTime->now->add( days => 1 );
+    ok(!(RT::Repeat::Ticket::Run->run('-date=' . $tomorrow->ymd)), 'Ran recurrence script for tomorrow.');
+    ok($m->goto_ticket($next_id), "Recurrence ticket $next_id created for tomorrow.");
+    $m->text_like( qr/Set up recurring aperture maintenance/);
+    $ticket = RT::Ticket->new(RT->SystemUser);
+    ok($ticket->Load($next_id), "Loaded ticket $next_id");
+    is($ticket->CreatedObj->ISO(Time => 0), $tomorrow->ymd, 'Created tomorrow');
+    is($ticket->StartsObj->ISO(Time => 0), $tomorrow->ymd, 'Starts tomorrow');
+    $tomorrow->add( days => 3 );
+    is($ticket->DueObj->ISO(Time => 0), $tomorrow->ymd, 'Due 3 days from tomorrow');
+
+    my $three_months = DateTime->now->add( months => 3 );
+    $next_id = $next_id + 1;
+    ok(!(RT::Repeat::Ticket::Run->run('-date=' . $three_months->ymd)), 'Ran recurrence script for 3 months from now.');
+    ok($m->goto_ticket($next_id), "Recurrence ticket $next_id created for 3 months from now.");
+    $m->text_like( qr/Set up recurring aperture maintenance/);
+    $ticket = RT::Ticket->new(RT->SystemUser);
+    ok($ticket->Load($next_id), "Loaded ticket $next_id");
+    is($ticket->CreatedObj->ISO(Time => 0), $three_months->ymd, 'Created 3 months from now');
+    is($ticket->StartsObj->ISO(Time => 0), $three_months->ymd, 'Starts 3 months from now');
+    $three_months->add( days => 3 );
+    is($ticket->DueObj->ISO(Time => 0), $three_months->ymd, 'Due 3 days from 3 months from now');
+
+    # a new ticket should be created even if there are existing tickets on the same day
+    $tomorrow = DateTime->now->add( days => 1 );
+    $next_id = $next_id + 1;
+    ok(!(RT::Repeat::Ticket::Run->run('-date=' . $tomorrow->ymd)), 'Ran recurrence script for tomorrow.');
+    ok($m->goto_ticket($next_id), "Recurrence ticket $next_id created for tomorrow.");
+    $m->text_like( qr/Set up recurring aperture maintenance/);
+    $ticket = RT::Ticket->new(RT->SystemUser);
+    ok($ticket->Load($next_id), "Loaded ticket $next_id");
+    is($ticket->CreatedObj->ISO(Time => 0), $tomorrow->ymd, 'Created tomorrow');
+    is($ticket->StartsObj->ISO(Time => 0), $tomorrow->ymd, 'Starts tomorrow');
+    $tomorrow->add( days => 3 );
+    is($ticket->DueObj->ISO(Time => 0), $tomorrow->ymd, 'Due 3 days from tomorrow');
+
+    $three_months = DateTime->now->add( months => 3 );
+    $next_id = $next_id + 1;
+    ok(!(RT::Repeat::Ticket::Run->run('-date=' . $three_months->ymd)), 'Ran recurrence script for 3 months from now.');
+    ok($m->goto_ticket($next_id), "Recurrence ticket $next_id created for 3 months from now.");
+    $m->text_like( qr/Set up recurring aperture maintenance/);
+    $ticket = RT::Ticket->new(RT->SystemUser);
+    ok($ticket->Load($next_id), "Loaded ticket $next_id");
+    is($ticket->CreatedObj->ISO(Time => 0), $three_months->ymd, 'Created 3 months from now');
+    is($ticket->StartsObj->ISO(Time => 0), $three_months->ymd, 'Starts 3 months from now');
+    $three_months->add( days => 3 );
+    is($ticket->DueObj->ISO(Time => 0), $three_months->ymd, 'Due 3 days from 3 months from now');
+}
+
+sub run_tests{
+    my ($baseurl, $m) = @_;
+
+    ok( $m->login( 'root', 'password' ), 'logged in' );
+
+    $m->submit_form_ok( { form_name => 'CreateTicketInQueue', }, 'Click to create ticket' );
+
+    $m->content_contains('Enable Recurrence');
+
+    diag "Create a ticket with a recurrence in the General queue.";
+
+    $m->submit_form_ok(
+        {   form_name => 'TicketCreate',
+            fields    => {
+                'Subject'                         => 'Set up recurring aperture maintenance',
+                'Content'                         => 'Perform work on portals once per day M - F',
+                'repeat-enabled'                  => 1,
+                'repeat-type'                     => 'daily',
+                'repeat-details-daily'            => 'day',
+                'repeat-details-daily-day'        => 1,
+                'repeat-create-on-recurring-date' => 1,
+                'repeat-coexistent-number'        => 0,
+                'repeat-lead-time'                => 3,
+                'repeat-start-date'               => DateTime->now->ymd,
+            },
+            button => 'SubmitTicket',
+        },
+        'Create'
+    );
+
+    $m->text_like( qr/Ticket\s(\d+)\screated in queue/);
+
+    my ($daily_id) = $m->content =~ /Ticket\s(\d+)\screated in queue/;
+    ok($daily_id, "Created ticket with id: $daily_id");
+
+    # resolving the parent ticket should have no affect on creating new tickets
+    my $ticket = RT::Ticket->new(RT->SystemUser);
+    ok($ticket->Load($daily_id), "Loaded ticket $daily_id");
+    ok($ticket->SetStatus('resolved'), "Ticket $daily_id resolved");
+
+    return $daily_id;
+}
+
+done_testing;

commit 2cca1e4a7cc79197963a846499e7ebd94e8ec748
Author: Brad Embree <brad at bestpractical.com>
Date:   Sun Aug 20 19:55:25 2023 -0700

    Add tests for simple mode
    
    Add tests that switching a repeat configuration to simple mode means a
    ticket will be created when one was not created in concurrent mode.

diff --git a/xt/weekly.t b/xt/weekly.t
index 37c2876..6ff095d 100644
--- a/xt/weekly.t
+++ b/xt/weekly.t
@@ -1,7 +1,7 @@
 use strict;
 use warnings;
 
-use RT::Extension::RepeatTicket::Test tests => 26;
+use RT::Extension::RepeatTicket::Test tests => 31;
 
 use_ok('RT::Extension::RepeatTicket');
 require_ok('bin/rt-repeat-ticket');
@@ -75,6 +75,18 @@ ok(!(RT::Repeat::Ticket::Run->run('-date=' . $thurs->ymd)), 'Ran recurrence scri
 my $ticket4 = RT::Ticket->new(RT->SystemUser);
 ok(!($ticket4->Load($third + 1)), 'No fourth ticket created.');
 
+diag "Run after changing create-on-recurring-date to 1";
+# change to create on recurring date and then a 4th ticket should be created
+ok( $m->goto_ticket($weekly_id), "Found ticket $weekly_id.");
+$m->follow_link_ok( {text => 'Recurrence'}, 'Loaded recurrence edit' );
+
+$m->form_name("ModifyRecurrence");
+$m->field('repeat-create-on-recurring-date' => 1);
+$m->click_button(name => 'SubmitTicket');
+$m->text_like( qr/Recurrence updated/);
+
+ok(!(RT::Repeat::Ticket::Run->run('-date=' . $thurs->ymd)), 'Ran recurrence script for next Thursday again.');
+ok($ticket4->Load($third + 1), 'Fourth ticket created after changing create-on-recurring-date to 1.');
 
 # Didn't want to add DateTime::Format::Natural as a dependency.
 sub GetThursday {

commit 2c65ad0bcaa5fb789d1df5e33590f67b0504eacd
Author: Brad Embree <brad at bestpractical.com>
Date:   Sun Aug 20 19:52:12 2023 -0700

    Update tests for new default configuration settings
    
    Simple mode will be the default which means the existing tests need to
    specify Concurrent Tickets Mode and the coexistent setting.

diff --git a/xt/cf.t b/xt/cf.t
index 7bb9ab0..8fb2e2c 100644
--- a/xt/cf.t
+++ b/xt/cf.t
@@ -33,6 +33,7 @@ $m->submit_form_ok(
             'repeat-type'                                          => 'daily',
             'repeat-details-daily'                                 => 'day',
             'repeat-details-daily-day'                             => 1,
+            'repeat-create-on-recurring-date'                      => 0,
         },
         button => 'SubmitTicket',
     },
diff --git a/xt/daily.t b/xt/daily.t
index e220327..73d81cc 100644
--- a/xt/daily.t
+++ b/xt/daily.t
@@ -88,12 +88,14 @@ sub run_tests{
     $m->submit_form_ok(
         {   form_name => 'TicketCreate',
             fields    => {
-                'Subject'                  => 'Set up recurring aperture maintenance',
-                'Content'                  => 'Perform work on portals once per day',
-                'repeat-enabled'           => 1,
-                'repeat-type'              => 'daily',
-                'repeat-details-daily'     => 'day',
-                'repeat-details-daily-day' => 1,
+                'Subject'                         => 'Set up recurring aperture maintenance',
+                'Content'                         => 'Perform work on portals once per day',
+                'repeat-enabled'                  => 1,
+                'repeat-type'                     => 'daily',
+                'repeat-details-daily'            => 'day',
+                'repeat-details-daily-day'        => 1,
+                'repeat-create-on-recurring-date' => 0,
+                'repeat-coexistent-number'        => 1,
             },
             button => 'SubmitTicket',
         },
diff --git a/xt/end_conditions.t b/xt/end_conditions.t
index 7f66a7a..8cd34ba 100644
--- a/xt/end_conditions.t
+++ b/xt/end_conditions.t
@@ -110,12 +110,14 @@ sub run_tests{
     $m->submit_form_ok(
         {   form_name => 'TicketCreate',
             fields    => {
-                'Subject'                  => 'Set up recurring aperture maintenance',
-                'Content'                  => 'Perform work on portals once per day',
-                'repeat-enabled'           => 1,
-                'repeat-type'              => 'daily',
-                'repeat-details-daily'     => 'day',
-                'repeat-details-daily-day' => 1,
+                'Subject'                         => 'Set up recurring aperture maintenance',
+                'Content'                         => 'Perform work on portals once per day',
+                'repeat-enabled'                  => 1,
+                'repeat-type'                     => 'daily',
+                'repeat-details-daily'            => 'day',
+                'repeat-details-daily-day'        => 1,
+                'repeat-create-on-recurring-date' => 0,
+                'repeat-coexistent-number'        => 1,
             },
             button => 'SubmitTicket',
         },
diff --git a/xt/monthly.t b/xt/monthly.t
index be27f1b..e501b04 100644
--- a/xt/monthly.t
+++ b/xt/monthly.t
@@ -30,6 +30,7 @@ $m->submit_form_ok(
             'repeat-type'                      => 'monthly',
             'repeat-details-monthly-day-day'   => $day->day,
             'repeat-details-monthly-day-month' => 1,
+            'repeat-create-on-recurring-date'  => 0,
         },
         button => 'SubmitTicket',
     },
diff --git a/xt/on_complete.t b/xt/on_complete.t
index cd97521..c6de3e5 100644
--- a/xt/on_complete.t
+++ b/xt/on_complete.t
@@ -26,6 +26,8 @@ $m->submit_form_ok(
             'repeat-type'                     => 'monthly',
             'repeat-details-monthly'          => 'complete',
             'repeat-details-monthly-complete' => 0,
+            'repeat-create-on-recurring-date' => 0,
+            'repeat-coexistent-number'        => 1,
         },
         button => 'SubmitTicket',
     },
diff --git a/xt/start_date.t b/xt/start_date.t
index c85620d..f6e21b0 100644
--- a/xt/start_date.t
+++ b/xt/start_date.t
@@ -22,16 +22,17 @@ diag "Repeat start date is: " . $day->ymd;
 $m->submit_form_ok(
     {   form_name => 'TicketCreate',
         fields    => {
-            'Subject'                     => 'Set up recurring aperture maintenance',
-            'Content'                     => 'Perform work on portals on Thursday',
-            'repeat-lead-time'            => 7,
-            'repeat-coexistent-number'    => 2,
-            'repeat-enabled'              => 1,
-            'repeat-type'                 => 'weekly',
-            'repeat-details-weekly'       => 'week',
-            'repeat-details-weekly-week'  => 1,
-            'repeat-details-weekly-weeks' => 'th',
-            'repeat-start-date'           => $day->ymd,
+            'Subject'                         => 'Set up recurring aperture maintenance',
+            'Content'                         => 'Perform work on portals on Thursday',
+            'repeat-lead-time'                => 7,
+            'repeat-coexistent-number'        => 2,
+            'repeat-enabled'                  => 1,
+            'repeat-type'                     => 'weekly',
+            'repeat-details-weekly'           => 'week',
+            'repeat-details-weekly-week'      => 1,
+            'repeat-details-weekly-weeks'     => 'th',
+            'repeat-start-date'               => $day->ymd,
+            'repeat-create-on-recurring-date' => 0,
         },
         button => 'SubmitTicket',
     },
diff --git a/xt/weekly.t b/xt/weekly.t
index 51790db..37c2876 100644
--- a/xt/weekly.t
+++ b/xt/weekly.t
@@ -19,15 +19,16 @@ diag "Create a ticket with a recurrence in the General queue.";
 $m->submit_form_ok(
     {   form_name => 'TicketCreate',
         fields    => {
-            'Subject'                     => 'Set up recurring aperture maintenance',
-            'Content'                     => 'Perform work on portals on Tuesday and Thursday',
-            'repeat-lead-time'            => 7,
-            'repeat-coexistent-number'    => 2,
-            'repeat-enabled'              => 1,
-            'repeat-type'                 => 'weekly',
-            'repeat-details-weekly'       => 'week',
-            'repeat-details-weekly-week'  => 1,
-            'repeat-details-weekly-weeks' => 'th',
+            'Subject'                         => 'Set up recurring aperture maintenance',
+            'Content'                         => 'Perform work on portals on Tuesday and Thursday',
+            'repeat-lead-time'                => 7,
+            'repeat-coexistent-number'        => 2,
+            'repeat-enabled'                  => 1,
+            'repeat-type'                     => 'weekly',
+            'repeat-details-weekly'           => 'week',
+            'repeat-details-weekly-week'      => 1,
+            'repeat-details-weekly-weeks'     => 'th',
+            'repeat-create-on-recurring-date' => 0,
         },
         button => 'SubmitTicket',
     },
diff --git a/xt/yearly.t b/xt/yearly.t
index 0d65f6a..c6359b7 100644
--- a/xt/yearly.t
+++ b/xt/yearly.t
@@ -28,6 +28,7 @@ $m->submit_form_ok(
             'repeat-type'                     => 'yearly',
             'repeat-details-yearly-day-month' => $day->month,
             'repeat-details-yearly-day-day'   => $day->day,
+            'repeat-create-on-recurring-date' => 0,
         },
         button => 'SubmitTicket',
     },

commit 462080fef8c82cee0874eb3bb7235dbce3110aad
Author: Brad Embree <brad at bestpractical.com>
Date:   Sun Aug 20 19:50:20 2023 -0700

    Update POD

diff --git a/lib/RT/Extension/RepeatTicket.pm b/lib/RT/Extension/RepeatTicket.pm
index d340e79..d458abe 100644
--- a/lib/RT/Extension/RepeatTicket.pm
+++ b/lib/RT/Extension/RepeatTicket.pm
@@ -861,10 +861,42 @@ Add this line:
 
 =back
 
+=head1 MODES
+
+=head2 Simple Mode VS Concurrent Tickets Mode
+
+This extension supports two different modes for the repeat ticket
+configurations. The extension originally only supported Concurrent
+Tickets Mode but many users found the logic counter intuitive.
+
+Any existing repeat ticket configurations from previous versions will be
+in Concurrent Tickets Mode unless the definition is changed.
+
+The default for new repeat ticket configurations is Simple Mode.
+
+=head3 Simple Mode
+
+In this mode tickets are created and start on the recurring date. If the
+lead time field is filled out the ticket will be due that many days
+after the recurring date. There is no check for existing active tickets
+and if the rt-repeat-ticket script is run multiple times for the same
+day it will create a new ticket for each run.
+
+=head3 Concurrent Tickets Mode
+
+In this mode the tickets are created with the due date as the recurring
+date. The tickets start on the due date minus the lead time. You can
+specify the max number of concurrent active tickets. If the
+rt-repeat-ticket script is run multiple times for the same day it will
+only create new tickets if there are fewer active tickets than the max
+number of concurrent active tickets.
+
 =head1 CONFIGURATION
 
 =head2 C<$RepeatTicketCoexistentNumber>
 
+Only used in Concurrent Tickets Mode.
+
 The C<$RepeatTicketCoexistentNumber>
 determines how many tickets can be in an active status for a
 recurrence at any time. A value of 1 means one ticket at a time can be active.
@@ -875,9 +907,13 @@ The extension default is 1 ticket.
 
 =head2 C<$RepeatTicketLeadTime>
 
-The C<$RepeatTicketLeadTime> becomes the ticket Starts value and sets how far
-in advance of a ticket's Due date you want the ticket to be created. This
-essentially is how long you want to give people to work on the ticket.
+When in Simple Mode the C<$RepeatTicketLeadTime> is the number of days
+to add to the recurring date for the Due date of the ticket.
+
+When in Concurrent Tickets Mode the C<$RepeatTicketLeadTime> becomes the
+ticket Starts value and sets how far in advance of a ticket's Due date
+you want the ticket to be created. This essentially is how long you want
+to give people to work on the ticket.
 
 For example, if you create a weekly recurrence scheduled on Mondays
 and set the lead time to 7 days, each Monday a ticket will be created
@@ -909,6 +945,16 @@ a new RT ColumnMap. You can see the available formats by looking at
 the columns available in the Display Columns portlet on the RT ticket
 search page.
 
+=head2 C<$RepeatTicketPreviewNumber>
+
+By default, the Recurrence Preview will show the next 5 tickets that will be
+created. You can modify the number of tickets to show by setting the
+C<$RepeatTicketPreviewNumber> option:
+
+    Set($RepeatTicketPreviewNumber, 10);
+
+Set the C<$RepeatTicketPreviewNumber> option to 0 to hide the Recurrence Preview.
+
 =head2 rt-repeat-ticket
 
 The rt-repeat-ticket utility evaluates all of your repeating tickets and creates
@@ -923,6 +969,11 @@ sure no repeating tickets have been missed. Just go back and run the script for
 the days you missed. You can also pass dates in the future which might be handy if
 you want to experiment with recurrences in a test environment.
 
+=head3 WARNING
+
+If you run the script multiple times for the same day then it is possible multiple
+tickets will be created for the same repeat ticket configuration.
+
 =head1 USAGE
 
 =head2 Initial Tickets
@@ -984,13 +1035,15 @@ Do you have rt-repeat-tickets scheduled in cron? Is it running?
 
 =item *
 
-Do you have previous tickets still in an active state? Resolve those tickets
-or increase the concurrent active tickets value.
+If the repeat configuration is in Concurrent Tickets Mode do you have
+previous tickets still in an active state? Resolve those tickets or
+increase the concurrent active tickets value.
 
 =item *
 
-Is it the right day? Remember to subtract the lead time value to determine
-the day new tickets should be created.
+Is it the right day? If the repeat configuration is in Concurrent
+Tickets Mode remember to subtract the lead time value to determine the
+day new tickets should be created.
 
 =item *
 
@@ -1013,6 +1066,16 @@ granted for "Original Ticket" custom field.
 
 =back
 
+=head1 SEARCHING
+
+To search for tickets that have recurrence enabled use the following in a Ticket
+Search:
+
+    HasAttribute = 'RepeatTicketSettings'
+
+This will need to be added on the Advanced tab so build the rest of your search
+as desired and then add the clause on the Advanced tab.
+
 =head1 METHODS
 
 =head2 Run( RT::Attribute $attr, DateTime $checkday )

commit 7beef3c52139b6ad40af0f30715bfdac907106b0
Author: Brad Embree <brad at bestpractical.com>
Date:   Sun Aug 20 19:49:48 2023 -0700

    Add Ticket Create Mode radio controls

diff --git a/html/Ticket/Elements/EditRecurrence b/html/Ticket/Elements/EditRecurrence
index 5e4c33c..fbfb751 100644
--- a/html/Ticket/Elements/EditRecurrence
+++ b/html/Ticket/Elements/EditRecurrence
@@ -1,4 +1,4 @@
-<script>
+<script type="text/javascript">
 jQuery( function () {
     jQuery('div.repeat input[name=repeat-enabled]').change( function () {
         var val = jQuery(this).val();
@@ -9,13 +9,23 @@ jQuery( function () {
             jQuery('div.repeat div.repeat-toggle').addClass('hidden');
         }
     } );
- 
+
     jQuery('div.repeat input[name=repeat-type]').change( function () {
         var val = jQuery(this).val();
         jQuery('div.repeat .repeat-details:not(.repeat-details-'+val+')').addClass('hidden');
         jQuery('div.repeat .repeat-details-' +val ).removeClass('hidden');
     } );
 
+    jQuery('div.repeat input[name=repeat-create-on-recurring-date]').change( function () {
+        var val = jQuery(this).val();
+        if ( val == 0 ) {
+            jQuery('div#repeat-coexistent-number-div').removeClass('hidden');
+        }
+        else {
+            jQuery('div#repeat-coexistent-number-div').addClass('hidden');
+        }
+    } );
+
 % if ( $ReadOnly ) {
     jQuery('div.repeat.read-only input, div.repeat.read-only select').attr('disabled', true);
 % }
@@ -39,6 +49,27 @@ jQuery( function () {
 
   <div class="repeat-toggle <% $ARGSRef->{'repeat-enabled'} ? '' : 'hidden' %> ">
 
+    <div class="form-row">
+      <div class="label input col-4">
+        <&|/l&>Ticket Create Mode</&>:
+      </div>
+      <div class="value col-8">
+        <div class="form-check form-check-inline">
+          <div class="custom-control custom-radio">
+            <input type="radio" id="<% $InputIdPrefix %>repeat-create-on-recurring-date-true" name="repeat-create-on-recurring-date" class="custom-control-input" value="1" <% $ARGSRef->{'repeat-create-on-recurring-date'} ? ' checked="checked"': '' |n %>>
+            <label class="custom-control-label" for="<% $InputIdPrefix %>repeat-create-on-recurring-date-true"><&|/l&>Simple Mode - Ticket created on Recurring Date</&></label>
+          </div>
+        </div>
+
+        <div class="form-check form-check-inline">
+          <div class="custom-control custom-radio">
+            <input type="radio" id="<% $InputIdPrefix %>repeat-create-on-recurring-date-false" name="repeat-create-on-recurring-date" class="custom-control-input" value="0" <% $ARGSRef->{'repeat-create-on-recurring-date'} ? '' : ' checked="checked"' |n %>>
+            <label class="custom-control-label" for="<% $InputIdPrefix %>repeat-create-on-recurring-date-false"><&|/l&>Concurrent Tickets Mode - Ticket due on Recurring Date</&></label>
+          </div>
+        </div>
+      </div>
+    </div>
+
     <div class="form-row">
       <div class="label input col-4">
         <&|/l&>Ticket lead time (days)</&>:
@@ -48,7 +79,7 @@ jQuery( function () {
       </div>
     </div>
 
-    <div class="form-row">
+    <div id="repeat-coexistent-number-div" class="form-row <% $ARGSRef->{'repeat-create-on-recurring-date'} ? 'hidden' : '' %>">
       <div class="label input col-4">
         <&|/l&>Concurrent active tickets</&>:
       </div>
@@ -250,10 +281,16 @@ $ARGSRef->{'repeat-details-monthly'} ||= 'day';
 $ARGSRef->{'repeat-details-yearly'} ||= 'day';
 $ARGSRef->{'repeat-end'} ||= 'none';
 $ARGSRef->{'repeat-lead-time'} //= RT->Config->Get('RepeatTicketLeadTime') // 14;
+$ARGSRef->{'repeat-create-on-recurring-date'} //= 1;
 
-# 0 is a valid value, so check for defined before setting to default
-if ( not defined $ARGSRef->{'repeat-coexistent-number'} ){
-    $ARGSRef->{'repeat-coexistent-number'} = RT->Config->Get('RepeatTicketCoexistentNumber') || 1;
+if ( $ARGSRef->{'repeat-create-on-recurring-date'} ) {
+    $ARGSRef->{'repeat-coexistent-number'} = 0;
+}
+else {
+    # 0 is a valid value, so check for defined before setting to default
+    if ( not defined $ARGSRef->{'repeat-coexistent-number'} ){
+        $ARGSRef->{'repeat-coexistent-number'} = RT->Config->Get('RepeatTicketCoexistentNumber') || 1;
+    }
 }
 
 my $input = sub {

commit 8d1c811106ea12ec335dab02a505ab92dacd9c1d
Author: Brad Embree <brad at bestpractical.com>
Date:   Sun Aug 20 19:45:02 2023 -0700

    Set Created date to Starts date
    
    This fixes an issue with the rt-repeat-ticket script when using the
    --date parameter. The ticket Created date should always be the ticket
    Starts date.

diff --git a/lib/RT/Extension/RepeatTicket.pm b/lib/RT/Extension/RepeatTicket.pm
index 37f4751..d340e79 100644
--- a/lib/RT/Extension/RepeatTicket.pm
+++ b/lib/RT/Extension/RepeatTicket.pm
@@ -452,6 +452,7 @@ sub Repeat {
             $due
             ? ( Due => $due->ISO )
             : (),
+            Created => $starts->ISO,
         );
 
         if ($id) {

commit 2d17122fe4e52022a3c8e385f8235b8d7c236a17
Author: Brad Embree <brad at bestpractical.com>
Date:   Sun Aug 20 19:43:50 2023 -0700

    Add lead time to Due date in Simple Mode
    
    If in simple mode add the lead time field value to the start date to get
    the Due date for the new ticket.

diff --git a/lib/RT/Extension/RepeatTicket.pm b/lib/RT/Extension/RepeatTicket.pm
index a583107..37f4751 100644
--- a/lib/RT/Extension/RepeatTicket.pm
+++ b/lib/RT/Extension/RepeatTicket.pm
@@ -436,6 +436,14 @@ sub Repeat {
         if ($set) {
             $due = RT::Date->new( RT->SystemUser );
             $due->Set( Format => 'unknown', Value => $checkday );
+
+            if (
+                defined $content->{'repeat-lead-time'}
+                &&
+                $content->{'repeat-create-on-recurring-date'}
+            ) {
+                $due->AddDays( $content->{'repeat-lead-time'} );
+            }
         }
 
         my ( $id, $txn, $msg ) = _RepeatTicket(

commit 6c07165ee04f2c1de9bffbf69dfc03870ad058aa
Author: Brad Embree <brad at bestpractical.com>
Date:   Sun Aug 20 19:40:12 2023 -0700

    Add logic to allow for new simple mode
    
    Add repeat-create-on-recurring-date attribute which indicates simple
    mode for the repeat configuration. The recurring date indicates the date
    the ticket should be created.
    
    It allows for, and assumes, repeat-coexistent-number is 0.

diff --git a/lib/RT/Extension/RepeatTicket.pm b/lib/RT/Extension/RepeatTicket.pm
index 2696c41..a583107 100644
--- a/lib/RT/Extension/RepeatTicket.pm
+++ b/lib/RT/Extension/RepeatTicket.pm
@@ -174,13 +174,19 @@ sub Repeat {
     my $repeat_ticket = $attr->Object;
 
     my $tickets_needed = TicketsToMeetCoexistentNumber($attr);
-    return unless $tickets_needed;
+
+    return unless $tickets_needed || $content->{'repeat-create-on-recurring-date'};
 
     for my $checkday (@checkdays) {
         # Adjust by lead time
         my $original_date = $checkday->clone();
-        $checkday = $checkday->add( days => $content->{'repeat-lead-time'} )
-          if defined $content->{'repeat-lead-time'};
+        if (
+            defined $content->{'repeat-lead-time'}
+            &&
+            ! $content->{'repeat-create-on-recurring-date'}
+        ) {
+            $checkday = $checkday->add( days => $content->{'repeat-lead-time'} );
+        }
         $RT::Logger->debug( 'Checking date ' . $original_date ->ymd .
                             ' with adjusted lead time date ' . $checkday->ymd );
 
@@ -238,7 +244,16 @@ sub Repeat {
             time_zone => RT->Config->Get('Timezone'),
         );
         $last_created->truncate( to => 'day' );
-        next unless $last_created->ymd lt $checkday->ymd;
+
+        # if we are in simple mode we do not care about the last ticket
+        # due date or create date
+        if ( $content->{'repeat-create-on-recurring-date'} ) {
+            # set last due to checkday so all sets start with the checkday
+            $last_due = $checkday;
+        }
+        else {
+            next unless $last_created->ymd lt $checkday->ymd;
+        }
 
         my $set;
         if ( $content->{'repeat-type'} eq 'daily' ) {
@@ -455,7 +470,10 @@ sub TicketsToMeetCoexistentNumber {
     my $attr    = shift;
     my $content = $attr->Content;
 
-    my $co_number = $content->{'repeat-coexistent-number'};
+    my $co_number
+        = $content->{'repeat-create-on-recurring-date'}
+            ? 0
+            : $content->{'repeat-coexistent-number'};
     $co_number = RT->Config->Get('RepeatTicketCoexistentNumber')
       unless defined $co_number && length $co_number;  # respect 0 but ''
     return unless $co_number;

-----------------------------------------------------------------------


hooks/post-receive
-- 
rt-extension-repeatticket


More information about the Bps-public-commit mailing list