Bug 42112: Add option when changing a patron's category: allow choosing what to do for the expiry date and messaging preferences

To test:
1. APPLY PATCH, updatedatabase, restart_all
2. Find 2 new system preferences UpdateDateExpiryOnCategoryChange and UpdateMessagingPrefsOnCategoryChange
3. Each was the same options:
    -Never update
    -Always ask, default to no
    -Always ask, default to yes
    -Always update without asking

4. Go to  Administration > Patron categories
5. Edit or create some categories with different Enrollment periods and messaaging preferences
6. Try each of the options for BOTH system preferences, making sure they work as described.

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Emily Lamancusa <emily.lamancusa@montgomerycountymd.gov>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This commit is contained in:
Lucas Gass 2026-03-26 15:11:21 +00:00 committed by Pedro Amorim
parent e1072702f8
commit 74130f08df
No known key found for this signature in database
GPG Key ID: 06B75BA7F3894D55
5 changed files with 147 additions and 14 deletions

View File

@ -0,0 +1,27 @@
use Modern::Perl;
use Koha::Installer::Output qw(say_warning say_success say_info);
return {
bug_number => "42112",
description => "Add UpdateExpiryDateOnCategoryChange and UpdateMessagingPrefsOnCategoryChange",
up => sub {
my ($args) = @_;
my ( $dbh, $out ) = @$args{qw(dbh out)};
$dbh->do(
q{
INSERT IGNORE INTO systempreferences (`variable`,`value`)
VALUES ('UpdateExpiryDateOnCategoryChange', 'softyes')
}
);
$dbh->do(
q{
INSERT IGNORE INTO systempreferences (`variable`,`value`)
VALUES ('UpdateMessagingPrefsOnCategoryChange', 'softyes')
}
);
say_success( $out, "System preference UpdateExpiryDateOnCategoryChange added." );
say_success( $out, "System preference UpdateMessagingPrefsOnCategoryChange added." );
},
};

View File

@ -868,11 +868,13 @@ INSERT INTO systempreferences ( `variable`, `value` ) VALUES
('UniqueItemFields','barcode'),
('UnseenRenewals','0'),
('UnsubscribeReflectionDelay',''),
('UpdateExpiryDateOnCategoryChange', 'softyes'),
('UpdateItemLocationOnCheckin', ''),
('UpdateItemLocationOnCheckout', ''),
('UpdateItemLostStatusWhenPaid', '0'),
('UpdateItemLostStatusWhenWriteoff', '0'),
('UpdateItemWhenLostFromHoldList',''),
('UpdateMessagingPrefsOnCategoryChange','softyes'),
('UpdateNotForLoanStatusOnCheckin', ''),
('UpdateNotForLoanStatusOnCheckout', ''),
('UpdateTotalIssuesOnCirc','0'),

View File

@ -99,6 +99,22 @@ Patrons:
1: Do
0: "Don't"
- charge a fee when a patron changes to a category with an enrollment fee.
-
- pref: UpdateExpiryDateOnCategoryChange
choices:
yes: "Always update without asking"
softyes: "Ask before updating, default to yes"
softno: "Ask before updating, default to no"
no: "Never update"
- the date expiry when changing an existing patron to a new category.
-
- pref: UpdateMessagingPrefsOnCategoryChange
choices:
yes: "Always update without asking"
softyes: "Ask before updating, default to yes"
softno: "Ask before updating, default to no"
no: "Never update"
- messaging preferences when changing an existing patron to a new category.
-
- Select which patron activities should be tracked to signify patron activity. Each time that one of these activities occurs, borrowers.lastseen will update with the current date and time.
- pref: TrackLastPatronActivityTriggers

View File

@ -1598,7 +1598,6 @@
</div>
<!-- /.main.container-fluid -->
<!-- Expiration date modal-->
<div id="categoryChangeModal" class="modal" tabindex="-1" role="dialog" aria-labelledby="categoryChangeModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
@ -1608,14 +1607,30 @@
</div>
<div class="modal-body">
<p>Would you like to apply the defaults for the new category?</p>
<div class="form-check">
<input type="checkbox" id="categoryChangeUpdateMessaging" />
<label class="form-check-label" for="categoryChangeUpdateMessaging"> Reset messaging preferences to category defaults </label>
</div>
<div class="form-check">
<input type="checkbox" id="categoryChangeUpdateExpiry" />
<label class="form-check-label" for="categoryChangeUpdateExpiry"> Reset expiration date to category default </label>
</div>
[% IF Koha.Preference('UpdateMessagingPrefsOnCategoryChange') == 'softyes' %]
<div class="form-check">
<input type="checkbox" id="categoryChangeUpdateMessaging" checked />
<label class="form-check-label" for="categoryChangeUpdateMessaging"> Reset messaging preferences to category defaults </label>
</div>
[% ELSIF Koha.Preference('UpdateMessagingPrefsOnCategoryChange') == 'softno' %]
<div class="form-check">
<input type="checkbox" id="categoryChangeUpdateMessaging" />
<label class="form-check-label" for="categoryChangeUpdateMessaging"> Reset messaging preferences to category defaults </label>
</div>
[% END %]
[% IF Koha.Preference('UpdateExpiryDateOnCategoryChange') == 'softno' || Koha.Preference('UpdateExpiryDateOnCategoryChange') == 'softyes' %]
[% IF Koha.Preference('UpdateExpiryDateOnCategoryChange') == 'softyes' %]
<div class="form-check">
<input type="checkbox" id="categoryChangeUpdateExpiry" checked />
<label class="form-check-label" for="categoryChangeUpdateExpiry"> Reset expiration date to category default </label>
</div>
[% ELSIF Koha.Preference('UpdateExpiryDateOnCategoryChange') == 'softno' %]
<div class="form-check">
<input type="checkbox" id="categoryChangeUpdateExpiry" />
<label class="form-check-label" for="categoryChangeUpdateExpiry"> Reset expiration date to category default </label>
</div>
[% END %]
[% END %]
</div>
<div class="modal-footer">
<button id="categoryChangeConfirmBtn" class="btn btn-primary"><i class="fa fa-check" aria-hidden="true"></i> Confirm</button>
@ -1920,6 +1935,9 @@
var TalkingTechItivaPhoneNotification = [% Koha.Preference('TalkingTechItivaPhoneNotification') || 0 | html %];
var PhoneNotification = [% Koha.Preference('PhoneNotification') || 0 | html %];
var UpdateExpiryDateOnCategoryChange = "[% Koha.Preference('UpdateExpiryDateOnCategoryChange') | html %]";
var UpdateMessagingPrefsOnCategoryChange = "[% Koha.Preference('UpdateMessagingPrefsOnCategoryChange') | html %]";
</script>
[% Asset.js("js/messaging-preference-form.js") | $raw %]
[% Asset.js("js/members.js") | $raw %]

View File

@ -7,16 +7,83 @@ $(document).ready(function () {
if (window.location.href.indexOf("op=add") === -1) {
message_prefs_dirty = true;
}
$("#categorycode_entry").change(function () {
var categorycode = $(this).val();
// if we change category, consider the messaging prefs dirty
message_prefs_dirty = true;
// Show the combined modal and reset checkboxes to checked
$("#categoryChangeUpdateMessaging").prop("checked", true);
$("#categoryChangeUpdateExpiry").prop("checked", true);
$("#categoryChangeModal").modal("show");
if (UpdateExpiryDateOnCategoryChange == "softyes") {
$("#categoryChangeUpdateExpiry").prop("checked", true);
} else if (UpdateExpiryDateOnCategoryChange == "softno") {
$("#categoryChangeUpdateExpiry").prop("checked", false);
}
if (UpdateMessagingPrefsOnCategoryChange == "softyes") {
$("#categoryChangeUpdateMessaging").prop("checked", true);
} else if (UpdateMessagingPrefsOnCategoryChange == "softno") {
$("#categoryChangeUpdateMessaging").prop("checked", false);
}
if (UpdateExpiryDateOnCategoryChange === "yes") {
// Just do it, no need to ask
var fp = $("#to").flatpickr();
var expiryDate = $(
"select" + category_selector + " option:selected"
).data("expiryDate");
if (expiryDate) {
fp.setDate(expiryDate.split("T")[0]);
}
}
if (UpdateMessagingPrefsOnCategoryChange === "yes") {
// Just do it, no need to ask
var messaging_prefs_loading = $("#messaging_prefs_loading");
messaging_prefs_loading.show();
$.getJSON(
"/cgi-bin/koha/members/default_messageprefs.pl?categorycode=" +
categorycode,
function (data) {
$.each(data.messaging_preferences, function (i, item) {
var attrid = item.message_attribute_id;
var transports = ["email", "rss", "sms"];
$.each(transports, function (j, transport) {
var checked = item["transports_" + transport] == 1;
$("#" + transport + attrid).prop(
"checked",
checked
);
toggle_digest(attrid);
});
if (item.digest && item.digest != " ") {
$("#digest" + attrid).prop("checked", true);
} else {
$("#digest" + attrid).prop("checked", false);
}
if (item.takes_days == "1") {
$("[name=" + attrid + "-DAYS]").val(
"" + item.days_in_advance
);
}
});
message_prefs_dirty = false;
}
).always(function () {
messaging_prefs_loading.hide();
});
}
var softUpdate =
UpdateMessagingPrefsOnCategoryChange === "softyes" ||
UpdateMessagingPrefsOnCategoryChange === "softno" ||
UpdateExpiryDateOnCategoryChange === "softyes" ||
UpdateExpiryDateOnCategoryChange === "softno";
// if softUpdate we should ask for confirmation in the modal, show it
if (softUpdate) {
$("#categoryChangeModal").modal("show");
}
// Remove any previously bound confirm handler to avoid stacking
$("#categoryChangeConfirmBtn")
.off("click")
@ -76,7 +143,10 @@ $(document).ready(function () {
}
// --- Update expiry date ---
if ($("#categoryChangeUpdateExpiry").prop("checked")) {
if (
UpdateExpiryDateOnCategoryChange !== "yes" &&
$("#categoryChangeUpdateExpiry").prop("checked")
) {
var fp = $("#to").flatpickr();
var expiryDate = $(
"select" + category_selector + " option:selected"