From 8e1b61c654be4beac4d73833446e228cff459cbd Mon Sep 17 00:00:00 2001 From: Martin Renvoize Date: Tue, 16 Sep 2025 13:47:45 +0100 Subject: [PATCH] Bug 40817: Add Koha object methods for hold-account line linking This patch implements the core object methods to support direct linking between holds and their associated account lines: Koha::Account.pm: - Enhance add_debit() to accept hold_id parameter for linking fees to holds - Add automatic detection of current vs old holds for proper field assignment - Add graceful handling of non-existent holds Koha::Account::Line.pm: - Add hold() method to retrieve associated hold (current or old) - Support bidirectional navigation between account lines and holds Koha::Hold.pm: - Add debits() method to get all charges associated with the hold - Update charge_hold_fee() to link created fees to the hold - Enhance _move_to_old() to migrate account line relationships - Add hold_id parameter to fee creation calls Koha::Old::Hold.pm: - Add debits() method for accessing charges linked to old holds --- Koha/Account.pm | 28 ++++++++++++++++++++++++++++ Koha/Account/Line.pm | 23 +++++++++++++++++++++++ Koha/Hold.pm | 41 +++++++++++++++++++++++++++++++++++++++-- Koha/Old/Hold.pm | 24 ++++++++++++++++++++++++ 4 files changed, 114 insertions(+), 2 deletions(-) diff --git a/Koha/Account.pm b/Koha/Account.pm index 0491325f5ae..f684509836c 100644 --- a/Koha/Account.pm +++ b/Koha/Account.pm @@ -492,6 +492,7 @@ sub add_debit { my $transaction_type = $params->{transaction_type}; my $item_id = $params->{item_id}; my $issue_id = $params->{issue_id}; + my $hold_id = $params->{hold_id}; my $old_issue_id; if ($issue_id) { @@ -503,6 +504,23 @@ sub add_debit { } } + my $old_hold_id; + if ($hold_id) { + my $hold = Koha::Holds->find($hold_id); + unless ($hold) { + my $old_hold = Koha::Old::Holds->find($hold_id); + if ($old_hold) { + $hold_id = undef; + $old_hold_id = $old_hold->id; + } else { + + # Neither current nor old hold exists, keep hold_id as is for now + # The caller will need to handle this case + $hold_id = undef; + } + } + } + my $line; my $schema = Koha::Database->new->schema; try { @@ -533,6 +551,16 @@ sub add_debit { ? ( old_issue_id => $old_issue_id ) : () ), + ( + $hold_id + ? ( reserve_id => $hold_id ) + : () + ), + ( + $old_hold_id + ? ( old_reserve_id => $old_hold_id ) + : () + ), branchcode => $library_id, register_id => $cash_register, ( diff --git a/Koha/Account/Line.pm b/Koha/Account/Line.pm index c796b92275a..e3ef80ca6d9 100644 --- a/Koha/Account/Line.pm +++ b/Koha/Account/Line.pm @@ -104,6 +104,29 @@ sub checkout { return $result; } +=head3 hold + + my $hold = $account_line->hold; + +Get the hold associated with this account line (if any) + +=cut + +sub hold { + my ($self) = @_; + + my $result; + if ( $self->reserve_id ) { + my $hold_rs = $self->_result->reserve; + $result = Koha::Hold->_new_from_dbic($hold_rs) if $hold_rs; + } elsif ( $self->old_reserve_id ) { + my $old_hold_rs = $self->_result->old_reserve; + $result = Koha::Old::Hold->_new_from_dbic($old_hold_rs) if $old_hold_rs; + } + + return $result; +} + =head3 library Returns a Koha::Library object representing where the accountline was recorded diff --git a/Koha/Hold.pm b/Koha/Hold.pm index 801d052fe82..ea2a0a15e36 100644 --- a/Koha/Hold.pm +++ b/Koha/Hold.pm @@ -855,7 +855,8 @@ sub cancel { interface => C4::Context->interface, library_id => C4::Context->userenv ? C4::Context->userenv->{'branch'} : undef, type => 'RESERVE_EXPIRED', - item_id => $self->itemnumber + item_id => $self->itemnumber, + hold_id => $self->id, } ) if $charge; } @@ -1221,6 +1222,7 @@ sub charge_hold_fee { description => $self->biblio->title, type => 'RESERVE', item_id => $self->itemnumber, + hold_id => $self->id, user_id => C4::Context->userenv ? C4::Context->userenv->{number} : undef, library_id => C4::Context->userenv ? C4::Context->userenv->{branch} : undef, interface => C4::Context->interface, @@ -1358,7 +1360,19 @@ sub _move_to_old { my ($self) = @_; my $hold_infos = $self->unblessed; require Koha::Old::Hold; - return Koha::Old::Hold->new($hold_infos)->store; + + # Create the old hold record + my $old_hold = Koha::Old::Hold->new($hold_infos)->store; + + # Update any linked accountlines to point to old_reserve_id instead of reserve_id + $self->_result->search_related('accountlines')->update( + { + old_reserve_id => $self->id, + reserve_id => undef, + } + ); + + return $old_hold; } =head3 to_api_mapping @@ -1450,6 +1464,29 @@ sub strings_map { return $strings; } +=head3 debits + + my $debits = $hold->debits; + +Get all debit account lines (charges) associated with this hold + +=cut + +sub debits { + my ($self) = @_; + + my $accountlines_rs = $self->_result->search_related( + 'accountlines', + { + credit_type_code => undef, # Only debits (no credits) + }, + { + order_by => { -desc => 'timestamp' }, + } + ); + return Koha::Account::Debits->_new_from_dbic($accountlines_rs); +} + =head2 Internal methods =head3 _type diff --git a/Koha/Old/Hold.pm b/Koha/Old/Hold.pm index 3d461e82a95..0eed0d2f15c 100644 --- a/Koha/Old/Hold.pm +++ b/Koha/Old/Hold.pm @@ -105,6 +105,30 @@ sub anonymize { return $self->update( { borrowernumber => $anonymous_id } ); } +=head3 debits + + my $debits = $old_hold->debits; + +Get all debit account lines (charges) associated with this old hold + +=cut + +sub debits { + my ($self) = @_; + + my $accountlines_rs = $self->_result->search_related( + 'accountlines', + { + credit_type_code => undef, # Only debits (no credits) + }, + { + # Optimize with proper ordering and potential prefetching + order_by => { -desc => 'timestamp' }, + } + ); + return Koha::Account::Debits->_new_from_dbic($accountlines_rs); +} + =head2 Internal methods =head3 _type -- 2.51.0