From 858ac03fea6466f47ab666b64a6bfb32775dfa3b Mon Sep 17 00:00:00 2001
From: Bill Erickson <berickxx@gmail.com>
Date: Thu, 17 Mar 2022 10:50:06 -0400
Subject: [PATCH] LP1891369 Renewal extension never shortens previous due date

Ensure the calculated due date of a renewal never occurs before the due
date of the previous circulation.

Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Michele Morgan <mmorgan@noblenet.org>
Signed-off-by: Galen Charlton <gmc@equinoxOLI.org>
---
 .../lib/OpenILS/Application/Circ/Circulate.pm      | 41 ++++++++++++++++------
 1 file changed, 31 insertions(+), 10 deletions(-)

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm
index ca293dcfa2..aec6471953 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm
@@ -2387,32 +2387,53 @@ sub extend_renewal_due_date {
     my $start_time = DateTime::Format::ISO8601->new
         ->parse_datetime(clean_ISO8601($prev_circ->xact_start))->epoch;
 
-    my $due_time = DateTime::Format::ISO8601->new
-        ->parse_datetime(clean_ISO8601($prev_circ->due_date))->epoch;
+    my $prev_due_date = DateTime::Format::ISO8601->new
+        ->parse_datetime(clean_ISO8601($prev_circ->due_date));
+
+    my $due_date = DateTime::Format::ISO8601->new
+        ->parse_datetime(clean_ISO8601($circ->due_date));
+
+    my $prev_due_time = $prev_due_date->epoch;
 
     my $now_time = DateTime->now->epoch;
 
-    return if $due_time < $now_time; # Renewed circ was overdue.
+    return if $prev_due_time < $now_time; # Renewed circ was overdue.
 
     if (my $interval = $matchpoint->renew_extend_min_interval) {
 
         my $min_duration = OpenILS::Utils::DateTime->interval_to_seconds($interval);
         my $checkout_duration = $now_time - $start_time;
 
-        return if $checkout_duration < $min_duration;
+        if ($checkout_duration < $min_duration) {
+            # Renewal occurred too early in the cycle to result in an
+            # extension of the due date on the renewal.
+
+            # If the new due date falls before the due date of
+            # the previous circulation, use the due date of the prev.
+            # circ so the patron does not lose time.
+            my $due = $due_date < $prev_due_date ? $prev_due_date : $due_date;
+            $circ->due_date($due->strftime('%FT%T%z'));
+
+            return;
+        }
     }
 
-    my $remaining_duration = $due_time - $now_time;
+    # Item was checked out long enough during the previous circulation
+    # to consider extending the due date of the renewal to cover the gap.
 
-    my $due_date = DateTime::Format::ISO8601->new
-        ->parse_datetime(clean_ISO8601($circ->due_date));
+    # Amount of the previous duration that was left unused.
+    my $remaining_duration = $prev_due_time - $now_time;
 
     $due_date->add(seconds => $remaining_duration);
 
-    $logger->info("circulator: extended renewal due date to $due_date");
+    # If the calculated due date falls before the due date of the previous 
+    # circulation, use the due date of the prev. circ so the patron does
+    # not lose time.
+    my $due = $due_date < $prev_due_date ? $prev_due_date : $due_date;
+
+    $logger->info("circulator: renewal due date extension landed on due date: $due");
 
-    # $circ->due_date is already in the needed timezone.
-    $circ->due_date($due_date->strftime('%FT%T%z'));
+    $circ->due_date($due->strftime('%FT%T%z'));
 }
 
 
-- 
2.11.0