From bacf6fb395b6f508bf6c60a7e84869d7ce923ef6 Mon Sep 17 00:00:00 2001 From: Jake Deery Date: Mon, 23 Feb 2026 17:25:22 +0000 Subject: [PATCH] Bug 27364: Add historic_cardnumbers to Patron object & API This patch adds an historic_cardnumbers field to the patron object, staff client, and API call (which will return an array of objects containing information about the cardnumber and when it was changed). TO TEST: == APPLY PATCH == a) Ensure 'CardnumberLog' is on in the sysprefs b) Change the koha user's cardnumber from 42 to 67 c) Reverse step b) d) Go to the koha user's details page *) note how there is a historic cardnumbers box *) note how each change is marked, next to the date and time performed *) note how the time is accurate to minutes e) Go to /api/v1/patrons/51 *) notice how the historic_cardnumbers array is populated with two objects *) each object is per cardnumber change *) each object contains the new and old cardnumber, as well as the timestamp of the transaction == RESET_ALL == f) repeat steps d-e *) notice how the historic cardnumbers section is missing if no cardnumber change has occured, on the patron details page *) notice how the historic_cardnumbers array is empty in the api call, if no cardnumber change has occured == SIGN OFF == --- Koha/Patron.pm | 45 ++++++++++++++++++- .../definitions/historic_cardnumbers.yaml | 18 ++++++++ api/v1/swagger/definitions/patron.yaml | 7 +++ .../prog/en/modules/members/moremember.tt | 19 ++++++++ members/moremember.pl | 6 +++ t/db_dependent/Koha/Patrons.t | 17 ++++++- 6 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 api/v1/swagger/definitions/historic_cardnumbers.yaml diff --git a/Koha/Patron.pm b/Koha/Patron.pm index c0ef6e292c5..2bc1bff87a3 100644 --- a/Koha/Patron.pm +++ b/Koha/Patron.pm @@ -21,7 +21,7 @@ package Koha::Patron; use Modern::Perl; use List::MoreUtils qw( any none uniq notall zip6); -use JSON qw( to_json ); +use JSON qw( from_json to_json ); use Scalar::Util qw(looks_like_number); use Unicode::Normalize qw( NFKD ); use Try::Tiny; @@ -33,6 +33,7 @@ use C4::Letters qw( GetPreparedLetter EnqueueLetter SendQueuedMessages ); use C4::Log qw( logaction ); use C4::Scrubber; use Koha::Account; +use Koha::ActionLogs; use Koha::ArticleRequests; use Koha::AuthUtils; use Koha::Caches; @@ -639,6 +640,46 @@ sub relationships_debt { return $non_issues_charges; } +=head3 historic_cardnumbers + +Returns a list of historic cardnumbers and their change date, or undef + +=cut + +sub historic_cardnumbers { + my ($self) = @_; + + my $logentries = Koha::ActionLogs->search( + { + module => 'MEMBERS', + action => 'MODIFY_CARDNUMBER', + object => $self->borrowernumber, + }, + { order_by => { -desc => 'timestamp' } } + )->unblessed or return; + + my @cardnumbers; + for my $logentry ( @{$logentries} ) { + my $info = from_json( $logentry->{info} ); + my $timestamp = $logentry->{timestamp}; + + next + unless $info->{before} + and $info->{after} + and $timestamp; + + my $object = { + previous_cardnumber => $info->{before}, + new_cardnumber => $info->{after}, + timestamp => $timestamp, + }; + + push @cardnumbers, $object; + } + + return \@cardnumbers; +} + =head3 housebound_profile Returns the HouseboundProfile associated with this patron. @@ -3020,6 +3061,8 @@ sub to_api { ? Mojo::JSON->true : Mojo::JSON->false; + $json_patron->{historic_cardnumbers} = $self->historic_cardnumbers; + return $json_patron; } diff --git a/api/v1/swagger/definitions/historic_cardnumbers.yaml b/api/v1/swagger/definitions/historic_cardnumbers.yaml new file mode 100644 index 00000000000..1803a3c1a3b --- /dev/null +++ b/api/v1/swagger/definitions/historic_cardnumbers.yaml @@ -0,0 +1,18 @@ +--- +type: object +properties: + previous_cardnumber: + description: The previous cardnumber of this patron + type: string + new_cardnumber: + description: The new cardnumber of this patron + type: string + timestamp: + description: The date and time of the cardnumber change + type: + - string +additionalProperties: false +required: + - previous_cardnumber + - new_cardnumber + - timestamp diff --git a/api/v1/swagger/definitions/patron.yaml b/api/v1/swagger/definitions/patron.yaml index afb252e1f17..b5873ad0445 100644 --- a/api/v1/swagger/definitions/patron.yaml +++ b/api/v1/swagger/definitions/patron.yaml @@ -408,6 +408,13 @@ properties: - "null" description: patron's primary contact method maxLength: 45 + historic_cardnumbers: + type: + - array + - "null" + description: patron's historic cardnumbers + items: + $ref: "historic_cardnumbers.yaml" _strings: type: - object diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt index cc59617307f..a9ed815723c 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt @@ -627,6 +627,25 @@ [%# /div#patron-consents %] [% END %] + [% IF historic_cardnumbers.size %] +
+
+

Historic cardnumbers

+
+
+
    + [% FOREACH historic_cardnumber IN historic_cardnumbers %] +
  1. + [% historic_cardnumber.timestamp | $KohaDates with_hours => 1 %]: + [% historic_cardnumber.previous_cardnumber | html %] → [% historic_cardnumber.new_cardnumber | html %] +
  2. + [% END %] +
+
+
+ [%# /div#historic-cardnumbers %] + [% END %] +
[% IF ( patron.B_phone || patron.B_email || patron.contactnote || patron.B_address || patron.B_address2 || patron.B_city || patron.B_zipcode || patron.B_country ) %]
diff --git a/members/moremember.pl b/members/moremember.pl index b64a58e3000..efb059d19f3 100755 --- a/members/moremember.pl +++ b/members/moremember.pl @@ -317,6 +317,12 @@ if ( C4::Context->preference('UseRecalls') ) { ); } +if ( C4::Context->preference('CardnumberLog') ) { + $template->param( + historic_cardnumbers => $patron->historic_cardnumbers, + ); +} + # Fetch available consent types and patron's consents my $consent_types = Koha::Patron::Consents->available_types; if ( keys %$consent_types ) { diff --git a/t/db_dependent/Koha/Patrons.t b/t/db_dependent/Koha/Patrons.t index 16ef2b22080..7965fcbd9a7 100755 --- a/t/db_dependent/Koha/Patrons.t +++ b/t/db_dependent/Koha/Patrons.t @@ -2029,7 +2029,7 @@ $retrieved_patron_1->delete; is( Koha::Patrons->search->count, $nb_of_patrons - 1, 'Delete should have deleted the patron' ); subtest 'BorrowersLog and CardnumberLog tests' => sub { - plan tests => 13; + plan tests => 16; t::lib::Mocks::mock_preference( 'BorrowersLog', 1 ); t::lib::Mocks::mock_preference( 'CardnumberLog', 0 ); @@ -2081,6 +2081,21 @@ subtest 'BorrowersLog and CardnumberLog tests' => sub { 'With CardnumberLog, one detailed MODIFY_CARDNUMBER action should be logged for the modification.' ); + ## test for historic_cardnumber accessor + my @historic_cardnumbers = @{ $patron->historic_cardnumbers }; + is( + $historic_cardnumbers[0]->{previous_cardnumber}, 'TESTCARDNUMBER', + 'previous_cardnumber in historic_cardnumbers[0] should be TESTCARDNUMBER' + ); + is( + $historic_cardnumbers[0]->{new_cardnumber}, 'TESTCARDNUMBER_1', + 'new_cardnumber in historic_cardnumbers[0] should be TESTCARDNUMBER_1' + ); + is( + exists( $historic_cardnumbers[0]->{timestamp} ), 1, + 'timestamp in historic_cardnumbers[0] should be set' + ); + t::lib::Mocks::mock_preference( 'BorrowersLog', 0 ); $patron->set( { cardnumber => 'TESTCARDNUMBER' } )->store; @logs = $schema->resultset('ActionLog') -- 2.50.1 (Apple Git-155)