From bf5da92ce5654d45dc26639d0c34cbffefe5bfc9 Mon Sep 17 00:00:00 2001 From: Jake Deery Date: Mon, 1 Dec 2025 13:09:20 +0000 Subject: [PATCH] Bug 14962: On Display code for BackgroundJobs This patch adds code required to make BackgroundJobs accept and process batch addition and removal of items to and from displays. Please see the test files commit for instructions on how to test this bundle of patches. --- Koha/BackgroundJob.pm | 2 + Koha/BackgroundJob/BatchAddDisplayItems.pm | 202 ++++++++++++++++++ Koha/BackgroundJob/BatchDeleteDisplayItems.pm | 173 +++++++++++++++ 3 files changed, 377 insertions(+) create mode 100644 Koha/BackgroundJob/BatchAddDisplayItems.pm create mode 100644 Koha/BackgroundJob/BatchDeleteDisplayItems.pm diff --git a/Koha/BackgroundJob.pm b/Koha/BackgroundJob.pm index 0ff204281d6..6d8955bcf9b 100644 --- a/Koha/BackgroundJob.pm +++ b/Koha/BackgroundJob.pm @@ -449,6 +449,8 @@ sub core_types_to_classes { batch_biblio_record_modification => 'Koha::BackgroundJob::BatchUpdateBiblio', batch_item_record_deletion => 'Koha::BackgroundJob::BatchDeleteItem', batch_item_record_modification => 'Koha::BackgroundJob::BatchUpdateItem', + batch_add_display_items => 'Koha::BackgroundJob::BatchAddDisplayItems', + batch_delete_display_items => 'Koha::BackgroundJob::BatchDeleteDisplayItems', erm_sushi_harvester => 'Koha::BackgroundJob::ErmSushiHarvester', batch_hold_cancel => 'Koha::BackgroundJob::BatchCancelHold', create_eholdings_from_biblios => 'Koha::BackgroundJob::CreateEHoldingsFromBiblios', diff --git a/Koha/BackgroundJob/BatchAddDisplayItems.pm b/Koha/BackgroundJob/BatchAddDisplayItems.pm new file mode 100644 index 00000000000..c98b89b7cf6 --- /dev/null +++ b/Koha/BackgroundJob/BatchAddDisplayItems.pm @@ -0,0 +1,202 @@ +package Koha::BackgroundJob::BatchAddDisplayItems; + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use Try::Tiny qw( catch try ); +use DateTime; +use C4::Context; + +use Koha::DisplayItem; +use Koha::DisplayItems; +use Koha::Items; +use Koha::Displays; + +use base 'Koha::BackgroundJob'; + +=head1 NAME + +Koha::BackgroundJob::BatchAddDisplayItems - Background job to add multiple items to a display + +=head1 API + +=head2 Class methods + +=head3 job_type + +Return the job type 'batch_add_display_items'. + +=cut + +sub job_type { + return 'batch_add_display_items'; +} + +=head3 process + +Process the batch addition of items to a display + +=cut + +sub process { + my ( $self, $args ) = @_; + + if ( $self->status eq 'cancelled' ) { + return; + } + + $self->start; + + my $display_id = $args->{display_id}; + my @item_ids = @{ $args->{item_ids} }; + my $date_remove = $args->{date_remove}; + + my $report = { + total_records => scalar @item_ids, + total_success => 0, + total_errors => 0, + }; + + my @messages; + my @added_items; + my @failed_items; + + # Validate display exists + my $display = Koha::Displays->find($display_id); + unless ($display) { + push @messages, { + type => 'error', + code => 'display_not_found', + display_id => $display_id, + }; + $self->finish( { messages => \@messages, report => $report } ); + return; + } + + # Calculate date_remove from display_days if not provided + unless ($date_remove) { + if ( $display->display_days ) { + my $dt = DateTime->now( time_zone => C4::Context->tz() ); + $dt->add( days => $display->display_days ); + $date_remove = $dt->ymd; + } + } + + try { + my $schema = Koha::Database->new->schema; + $schema->txn_do( + sub { + for my $item_id ( sort { $a <=> $b } @item_ids ) { + + last if $self->get_from_storage->status eq 'cancelled'; + + my $item = Koha::Items->find($item_id); + unless ($item) { + push @failed_items, { + itemnumber => $item_id, + error => 'Item not found' + }; + $report->{total_errors}++; + next; + } + + # Check if item is already in this display + my $existing = Koha::DisplayItems->search( + { + display_id => $display_id, + itemnumber => $item_id, + } + )->next; + + if ($existing) { + push @failed_items, { + itemnumber => $item_id, + error => 'Item already in display' + }; + $report->{total_errors}++; + next; + } + + # Create display item + my $display_item = Koha::DisplayItem->new( + { + display_id => $display_id, + itemnumber => $item_id, + biblionumber => $item->biblionumber, + date_remove => $date_remove, + } + )->store; + + push @added_items, { + display_item_id => $display_item->display_item_id, + itemnumber => $item_id, + biblionumber => $item->biblionumber, + }; + + $report->{total_success}++; + $self->step; + } + } + ); + } catch { + warn $_; + push @messages, { + type => 'error', + code => 'unknown', + error => $_, + }; + die "Something terrible has happened!" if ( $_ =~ /Rollback failed/ ); + }; + + $report->{added_items} = \@added_items; + $report->{failed_items} = \@failed_items; + + my $data = $self->decoded_data; + $data->{messages} = \@messages; + $data->{report} = $report; + + $self->finish($data); +} + +=head3 enqueue + +Enqueue the job. + +=cut + +sub enqueue { + my ( $self, $args ) = @_; + + return unless exists $args->{item_ids} && exists $args->{display_id}; + + my @item_ids = @{ $args->{item_ids} }; + my $display_id = $args->{display_id}; + my $date_remove = $args->{date_remove}; + + $self->SUPER::enqueue( + { + job_size => scalar @item_ids, + job_args => { + display_id => $display_id, + item_ids => \@item_ids, + date_remove => $date_remove, + }, + job_queue => 'long_tasks', + } + ); +} + +1; diff --git a/Koha/BackgroundJob/BatchDeleteDisplayItems.pm b/Koha/BackgroundJob/BatchDeleteDisplayItems.pm new file mode 100644 index 00000000000..606f9fabd36 --- /dev/null +++ b/Koha/BackgroundJob/BatchDeleteDisplayItems.pm @@ -0,0 +1,173 @@ +package Koha::BackgroundJob::BatchDeleteDisplayItems; + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use Try::Tiny qw( catch try ); + +use Koha::DisplayItem; +use Koha::DisplayItems; +use Koha::Items; + +use base 'Koha::BackgroundJob'; + +=head1 NAME + +Koha::BackgroundJob::BatchDeleteDisplayItems - Background job to remove multiple items from displays + +=head1 API + +=head2 Class methods + +=head3 job_type + +Return the job type 'batch_delete_display_items'. + +=cut + +sub job_type { + return 'batch_delete_display_items'; +} + +=head3 process + +Process the batch removal of items from displays + +=cut + +sub process { + my ( $self, $args ) = @_; + + if ( $self->status eq 'cancelled' ) { + return; + } + + $self->start; + + my @item_ids = @{ $args->{item_ids} }; + my $display_id = $args->{display_id}; # Optional: if specified, only remove from this display + + my $report = { + total_records => scalar @item_ids, + total_success => 0, + total_errors => 0, + }; + + my @messages; + my @deleted_items; + my @failed_items; + + try { + my $schema = Koha::Database->new->schema; + $schema->txn_do( + sub { + for my $item_id ( sort { $a <=> $b } @item_ids ) { + + last if $self->get_from_storage->status eq 'cancelled'; + + # Build search criteria + my $search_criteria = { itemnumber => $item_id }; + $search_criteria->{display_id} = $display_id if $display_id; + + my $display_items = Koha::DisplayItems->search($search_criteria); + + unless ($display_items->count) { + push @failed_items, { + itemnumber => $item_id, + error => $display_id ? 'Item not found in specified display' : 'Item not found in any display' + }; + $report->{total_errors}++; + next; + } + + my $deleted_count = 0; + my @deleted_from_displays; + + while (my $display_item = $display_items->next) { + push @deleted_from_displays, { + display_id => $display_item->display_id, + display_item_id => $display_item->display_item_id, + }; + $display_item->delete; + $deleted_count++; + } + + if ($deleted_count > 0) { + push @deleted_items, { + itemnumber => $item_id, + displays_removed_from => \@deleted_from_displays, + count => $deleted_count, + }; + $report->{total_success}++; + } else { + push @failed_items, { + itemnumber => $item_id, + error => 'No display items could be deleted' + }; + $report->{total_errors}++; + } + + $self->step; + } + } + ); + } catch { + warn $_; + push @messages, { + type => 'error', + code => 'unknown', + error => $_, + }; + die "Something terrible has happened!" if ( $_ =~ /Rollback failed/ ); + }; + + $report->{deleted_items} = \@deleted_items; + $report->{failed_items} = \@failed_items; + + my $data = $self->decoded_data; + $data->{messages} = \@messages; + $data->{report} = $report; + + $self->finish($data); +} + +=head3 enqueue + +Enqueue the job. + +=cut + +sub enqueue { + my ( $self, $args ) = @_; + + return unless exists $args->{item_ids}; + + my @item_ids = @{ $args->{item_ids} }; + my $display_id = $args->{display_id}; # Optional + + $self->SUPER::enqueue( + { + job_size => scalar @item_ids, + job_args => { + item_ids => \@item_ids, + display_id => $display_id, + }, + job_queue => 'long_tasks', + } + ); +} + +1; \ No newline at end of file -- 2.50.1 (Apple Git-155)