Bug 41358: Always store circulation logs in consistent JSON format

Previously, CIRCULATION ISSUE logs would store just the itemnumber when
there were no confirmations or forced overrides, but would store a full
JSON structure when overrides were present. This inconsistency makes
reporting difficult as queries need to handle both formats.

This patch changes AddIssue to always store logs in JSON format,
matching the pattern already used in AddRenewal. The JSON structure
now consistently includes:
- issue: issue_id
- branchcode: branchcode
- itemnumber: itemnumber
- confirmations: array of confirmation codes (empty array if none)
- forced: array of forced override codes (empty array if none)

Benefits:
- Consistent data format simplifies reporting and analysis
- Matches existing RENEWAL logging pattern
- Display code (tools/viewlog.pl) already handles both formats,
  so backward compatibility is maintained

Test plan:
1. prove t/db_dependent/Circulation.t
2. Perform a normal checkout - verify action log contains JSON
3. Perform a checkout with overrides - verify confirmations/forced
   are populated in the JSON
4. Check Tools > Log viewer - both types should display correctly

Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This commit is contained in:
Martin Renvoize 2025-12-18 14:47:39 +00:00 committed by Lucas Gass
parent abd436ecf6
commit 63e4f77edd
No known key found for this signature in database
GPG Key ID: 60B447398B0D36CA
2 changed files with 20 additions and 19 deletions

View File

@ -2021,19 +2021,16 @@ sub AddIssue {
# Log the checkout
if ( C4::Context->preference('IssueLog') ) {
my $info = $item_object->itemnumber;
if ( defined($confirmations) || defined($forced) ) {
$info = to_json(
{
issue => $issue->issue_id,
branchcode => $issue->branchcode,
itemnumber => $item_object->itemnumber,
confirmations => $confirmations,
forced => $forced
},
{ pretty => 1, canonical => 1 }
);
}
my $info = to_json(
{
issue => $issue->issue_id,
branchcode => $issue->branchcode,
itemnumber => $item_object->itemnumber,
confirmations => $confirmations || [],
forced => $forced || []
},
{ pretty => 1, canonical => 1 }
);
logaction(
"CIRCULATION", "ISSUE",
$patron->borrowernumber,

View File

@ -8317,7 +8317,7 @@ subtest 'ChildNeedsGuarantor' => sub {
};
subtest 'Bug 9762: AddIssue override JSON logging' => sub {
plan tests => 8;
plan tests => 11;
my $library = $builder->build_object( { class => 'Koha::Libraries' } );
my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
@ -8329,7 +8329,7 @@ subtest 'Bug 9762: AddIssue override JSON logging' => sub {
# Clear any existing logs
Koha::ActionLogs->search( { module => 'CIRCULATION', action => 'ISSUE' } )->delete;
# Test 1: Normal checkout without overrides - should log item number only
# Test 1: Normal checkout without overrides - should log JSON with empty arrays
my $issue = C4::Circulation::AddIssue( $patron, $item->barcode );
ok( $issue, 'Item checked out successfully' );
@ -8342,8 +8342,12 @@ subtest 'Bug 9762: AddIssue override JSON logging' => sub {
{ order_by => { -desc => 'timestamp' } }
);
is( $logs->count, 1, 'One log entry created for normal checkout' );
my $log = $logs->next;
is( $log->info, $item->itemnumber, 'Normal checkout logs item number only' );
my $log = $logs->next;
my $log_data = eval { from_json( $log->info ) };
ok( !$@, 'Normal checkout log info is valid JSON' );
is( $log_data->{itemnumber}, $item->itemnumber, 'JSON contains correct itemnumber' );
is_deeply( $log_data->{confirmations}, [], 'Confirmations is empty array for normal checkout' );
is_deeply( $log_data->{forced}, [], 'Forced is empty array for normal checkout' );
# Return the item for next test
C4::Circulation::AddReturn( $item->barcode, $library->branchcode );
@ -8367,14 +8371,14 @@ subtest 'Bug 9762: AddIssue override JSON logging' => sub {
module => 'CIRCULATION',
action => 'ISSUE',
object => $patron->borrowernumber,
info => { '!=', $item->itemnumber } # Exclude the first test log
info => { 'LIKE', '%' . $item2->itemnumber . '%' } # Find the second test log by itemnumber
},
{ order_by => { -desc => 'timestamp' } }
);
is( $logs->count, 1, 'One log entry created for override checkout' );
$log = $logs->next;
my $log_data = eval { from_json( $log->info ) };
$log_data = eval { from_json( $log->info ) };
ok( !$@, 'Log info is valid JSON' );
ok( exists $log_data->{confirmations}, 'JSON contains confirmations array' );
is_deeply( $log_data->{confirmations}, [ 'DEBT', 'AGE_RESTRICTION' ], 'Confirmations logged correctly' );