Koha/t/db_dependent/Members/IssueSlip.t
David Gustafsson f726558510
Bug 32496: Reduce unnecessary unblessings of objects in Circulation.pm
Refactor the most performance critical subroutines in Circulation.pm
to take objects instead of unblessed ones to reduce unnecessary
unblessings and generally clean up the code.

To test:

1) Ensure the following tests all pass:
  t/db_dependent/Circulation.t
  t/db_dependent/Circulation/CalcDateDue.t
  t/db_dependent/Circulation/CheckIfIssuedToPatron.t
  t/db_dependent/Circulation/GetPendingOnSiteCheckouts.t
  t/db_dependent/Circulation/GetTopIssues.t
  t/db_dependent/Circulation/IsItemIssued.t
  t/db_dependent/Circulation/MarkIssueReturned.t
  t/db_dependent/Circulation/ReturnClaims.t
  t/db_dependent/Circulation/Returns.t
  t/db_dependent/Circulation/SwitchOnSiteCheckouts.t
  t/db_dependent/Circulation/TooMany.t
  t/db_dependent/Circulation/dateexpiry.t
  t/db_dependent/Circulation/issue.t
  t/db_dependent/Circulation/maxsuspensiondays.t
  t/db_dependent/Circulation/transferbook.t
  t/db_dependent/Circulation_holdsqueue.t
  t/db_dependent/DecreaseLoanHighHolds.t
  t/db_dependent/Holds/DisallowHoldIfItemsAvailable.t
  t/db_dependent/Holds/RevertWaitingStatus.t
  t/db_dependent/ILSDI_Services.t
  t/db_dependent/Illrequests.t
  t/db_dependent/Koha/Account/Line.t
  t/db_dependent/Koha/Biblio.t
  t/db_dependent/Koha/Items.t
  t/db_dependent/Koha/Object.t
  t/db_dependent/Koha/Patrons.t
  t/db_dependent/Koha/Pseudonymization.t
  t/db_dependent/Koha/Template/Plugin/CirculationRules.t
  t/db_dependent/Letters/TemplateToolkit.t
  t/db_dependent/Members/GetAllIssues.t
  t/db_dependent/Members/IssueSlip.t
  t/db_dependent/Patron/Borrower_Discharge.t
  t/db_dependent/Patron/Borrower_PrevCheckout.t
  t/db_dependent/SIP/ILS.t
  t/db_dependent/Holds.t
  t/db_dependent/Holds/LocalHoldsPriority.t
  t/db_dependent/Holds/HoldFulfillmentPolicy.t
  t/db_dependent/Holds/HoldItemtypeLimit.t
  t/db_dependent/Reserves/GetReserveFee.t
  t/db_dependent/api/v1/return_claims.t
  t/db_dependent/api/v1/biblios.t
  t/db_dependent/api/v1/checkouts.t
  t/db_dependent/Reserves.t
  t/db_dependent/HoldsQueue.t
  t/db_dependent/selenium/regressions.t
  t/db_dependent/Koha/Plugins/Circulation_hooks.t
  t/db_dependent/Koha/Plugins/Recall_hooks.t
  t/db_dependent/Koha/Recalls.t
  t/db_dependent/Koha/Recall.t
  t/db_dependent/Circulation/_CalculateAndUpdateFine.t

Sponsored-by: Gothenburg University Library

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>

Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
2023-09-22 10:52:39 -03:00

427 lines
14 KiB
Perl
Executable file

#!/usr/bin/perl
# 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 <http://www.gnu.org/licenses>.
use Modern::Perl;
use Data::Dumper; # REMOVEME with diag
use Test::More tests => 3;
use Test::MockModule;
use Test::MockTime qw( set_fixed_time );
use t::lib::TestBuilder;
use C4::Circulation qw( AddIssue AddReturn );
use C4::Biblio qw( AddBiblio );
use C4::Items;
use C4::Members qw( IssueSlip );
use Koha::DateUtils qw( dt_from_string output_pref );
use Koha::Library;
use Koha::Patrons;
use DateTime::Duration;
use MARC::Record;
my $schema = Koha::Database->schema;
$schema->storage->txn_begin;
my $dbh = C4::Context->dbh;
$dbh->do(q|DELETE FROM issues|);
$dbh->do(q|DELETE FROM borrowers|);
$dbh->do(q|DELETE FROM items|);
$dbh->do(q|DELETE FROM biblio|);
$dbh->do(q|DELETE FROM categories|);
$dbh->do(q|DELETE FROM letter|);
my $builder = t::lib::TestBuilder->new;
set_fixed_time(CORE::time());
my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode };
my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
my $itemtype = $builder->build({ source => 'Itemtype' })->{ itemtype };
my %item_infos = (
homebranch => $branchcode,
holdingbranch => $branchcode,
itype => $itemtype
);
my $slip_content = <<EOS;
Checked out:
<checkedout>
Title: <<biblio.title>>
Barcode: <<items.barcode>>
Date due: <<issues.date_due>>
</checkedout>
Overdues:
<overdue>
Title: <<biblio.title>>
Barcode: <<items.barcode>>
Date due: <<issues.date_due>>
</overdue>
EOS
$dbh->do(q|
INSERT INTO letter( module, code, branchcode, name, is_html, title, content, message_transport_type) VALUES ('circulation', 'ISSUESLIP', '', 'Issue Slip', 0, 'Issue Slip', ?, 'email')
|, {}, $slip_content);
my $quick_slip_content = <<EOS;
Checked out today:
<checkedout>
Title: <<biblio.title>>
Barcode: <<items.barcode>>
Date due: <<issues.date_due>>
</checkedout>
EOS
$dbh->do(q|
INSERT INTO letter( module, code, branchcode, name, is_html, title, content, message_transport_type) VALUES ('circulation', 'ISSUEQSLIP', '', 'Issue Quick Slip', 0, 'Issue Quick Slip', ?, 'email')
|, {}, $quick_slip_content);
my ( $title1, $title2 ) = ( 'My title 1', 'My title 2' );
my ( $barcode1, $barcode2 ) = ('BC0101', 'BC0202' ); # FIXME Must not be hardcoded, tests could fail
my $record = MARC::Record->new;
$record->append_fields(
MARC::Field->new(
245, '1', '4',
a => $title1,
),
);
my ($biblionumber1) = AddBiblio( $record, '' );
my $itemnumber1 = $builder->build_sample_item({ biblionumber => $biblionumber1, barcode => $barcode1, %item_infos })->itemnumber;
$record = MARC::Record->new;
$record->append_fields(
MARC::Field->new(
245, '1', '4',
a => $title2,
),
);
my ($biblionumber2) = AddBiblio( $record, '' );
my $itemnumber2 = $builder->build_sample_item({ biblionumber => $biblionumber2, barcode => $barcode2, %item_infos })->itemnumber;
my $borrowernumber =
Koha::Patron->new({ categorycode => $categorycode, branchcode => $branchcode })->store->borrowernumber;
my $borrower = Koha::Patrons->find( $borrowernumber );
my $module = Test::MockModule->new('C4::Context');
$module->mock( 'userenv', sub { { branch => $branchcode } } );
my $today = dt_from_string;
my $yesterday = dt_from_string->subtract_duration( DateTime::Duration->new( days => 1 ) );
subtest 'Issue slip' => sub {
plan tests => 3;
subtest 'Empty slip' => sub {
plan tests => 1;
my $slip = IssueSlip( $branchcode, $borrowernumber );
my $empty_slip = <<EOS;
Checked out:
Overdues:
EOS
is( $slip->{content}, $empty_slip, 'No checked out or overdues return an empty slip, it should return undef (FIXME)' );
};
subtest 'Daily loans' => sub {
if ( $today->hour == 23 and $today->minute == 59 ) {
plan skip_all => "It's 23:59!";
} else {
plan tests => 2;
}
# Test 1: No overdue
my $today_daily = $today->clone->set( hour => 23, minute => 59 );
my $today_daily_as_formatted = output_pref( $today_daily );
my $yesterday_daily = $yesterday->clone->set( hour => 23, minute => 59 );
my $yesterday_daily_as_formatted = output_pref( $yesterday_daily );
my ( $date_due, $issue_date, $slip, $expected_slip );
$date_due = $today_daily;
$issue_date = $today_daily->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
AddIssue( $borrower, $barcode1, $date_due, undef, $issue_date );
$date_due = $today_daily;
$issue_date = $yesterday_daily;
AddIssue( $borrower, $barcode2, $date_due, undef, $issue_date );
# Set timestamps to the same value to avoid a different order
Koha::Checkouts->search(
{ borrowernumber => $borrower->borrowernumber }
)->update( { timestamp => dt_from_string } );
$expected_slip = <<EOS;
Checked out:
Title: $title1
Barcode: $barcode1
Date due: $today_daily_as_formatted
Title: $title2
Barcode: $barcode2
Date due: $today_daily_as_formatted
Overdues:
EOS
$slip = IssueSlip( $branchcode, $borrowernumber );
is( $slip->{content}, $expected_slip , 'IssueSlip should return a slip with 2 checkouts');
AddReturn( $barcode1, $branchcode );
AddReturn( $barcode2, $branchcode );
# Test 2: 1 Overdue
$date_due = $today_daily;
$issue_date = $today_daily->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
AddIssue( $borrower, $barcode1, $date_due, undef, $issue_date );
$date_due = $yesterday_daily;
$issue_date = $yesterday_daily;
AddIssue( $borrower, $barcode2, $date_due, undef, $issue_date );
$expected_slip = <<EOS;
Checked out:
Title: $title1
Barcode: $barcode1
Date due: $today_daily_as_formatted
Overdues:
Title: $title2
Barcode: $barcode2
Date due: $yesterday_daily_as_formatted
EOS
$slip = IssueSlip( $branchcode, $borrowernumber );
is( $slip->{content}, $expected_slip, 'IssueSlip should return a slip with 1 checkout and 1 overdue');
AddReturn( $barcode1, $branchcode );
AddReturn( $barcode2, $branchcode );
};
subtest 'Hourly loans' => sub {
if ( $today->hour == 23 and $today->minute == 59 ) {
plan skip_all => "It's 23:59!";
} else {
plan tests => 2;
}
# Test 1: No overdue
my ( $date_due_in_time, $date_due_in_time_as_formatted, $date_due_in_late, $date_due_in_late_as_formatted, $issue_date, $slip, $expected_slip );
# Assuming today is not hour = 23 and minute = 59
$date_due_in_time = $today->clone->set(hour => ($today->hour < 23 ? $today->hour + 1 : 23), minute => 59);
$date_due_in_time_as_formatted = output_pref( $date_due_in_time );
$issue_date = $date_due_in_time->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
AddIssue( $borrower, $barcode1, $date_due_in_time, undef, $issue_date );
$issue_date = $yesterday->clone;
AddIssue( $borrower, $barcode2, $date_due_in_time, undef, $issue_date );
# Set timestamps to the same value to avoid a different order
Koha::Checkouts->search(
{ borrowernumber => $borrower->borrowernumber }
)->update( { timestamp => dt_from_string } );
$expected_slip = <<EOS;
Checked out:
Title: $title1
Barcode: $barcode1
Date due: $date_due_in_time_as_formatted
Title: $title2
Barcode: $barcode2
Date due: $date_due_in_time_as_formatted
Overdues:
EOS
$slip = IssueSlip( $branchcode, $borrowernumber );
is( $slip->{content}, $expected_slip, 'IssueSlip should return a slip with 2 checkouts' )
or diag(Dumper(Koha::Checkouts->search({borrowernumber => $borrower->{borrowernumber}})->unblessed));
AddReturn( $barcode1, $branchcode );
AddReturn( $barcode2, $branchcode );
# Test 2: 1 Overdue
$date_due_in_time = $today->clone->set(hour => ($today->hour < 23 ? $today->hour + 1 : 23), minute => 59);
$date_due_in_time_as_formatted = output_pref( $date_due_in_time );
$issue_date = $date_due_in_time->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
AddIssue( $borrower, $barcode1, $date_due_in_time, undef, $issue_date );
$date_due_in_late = $today->clone->subtract( hours => 1 );
$date_due_in_late_as_formatted = output_pref( $date_due_in_late );
$issue_date = $yesterday->clone;
AddIssue( $borrower, $barcode2, $date_due_in_late, undef, $issue_date );
$expected_slip = <<EOS;
Checked out:
Title: $title1
Barcode: $barcode1
Date due: $date_due_in_time_as_formatted
Overdues:
Title: $title2
Barcode: $barcode2
Date due: $date_due_in_late_as_formatted
EOS
$slip = IssueSlip( $branchcode, $borrowernumber );
is( $slip->{content}, $expected_slip, 'IssueSlip should return a slip with 1 checkout and 1 overdue' );
AddReturn( $barcode1, $branchcode );
AddReturn( $barcode2, $branchcode );
};
};
subtest 'Quick slip' => sub {
plan tests => 3;
subtest 'Empty slip' => sub {
plan tests => 1;
my $slip = IssueSlip( $branchcode, $borrowernumber, 'quick slip' );
my $empty_slip = <<EOS;
Checked out today:
EOS
is( $slip->{content}, $empty_slip, 'No checked out or overdues return an empty slip, it should return undef (FIXME)' );
};
subtest 'Daily loans' => sub {
if ( $today->hour == 23 and $today->minute == 59 ) {
plan skip_all => "It's 23:59!";
} else {
plan tests => 2;
}
# Test 1: No overdue
my $today_daily = $today->clone->set( hour => 23, minute => 59 );
my $today_daily_as_formatted = output_pref( $today_daily );
my $yesterday_daily = $yesterday->clone->set( hour => 23, minute => 59 );
my $yesterday_daily_as_formatted = output_pref( $yesterday_daily );
my ( $date_due, $issue_date, $slip, $expected_slip );
$date_due = $today_daily;
$issue_date = $today_daily->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
AddIssue( $borrower, $barcode1, $date_due, undef, $issue_date );
$date_due = $today_daily;
$issue_date = $yesterday_daily;
AddIssue( $borrower, $barcode2, $date_due, undef, $issue_date );
$expected_slip = <<EOS;
Checked out today:
Title: $title1
Barcode: $barcode1
Date due: $today_daily_as_formatted
EOS
$slip = IssueSlip( $branchcode, $borrowernumber, 'quick slip' );
is( $slip->{content}, $expected_slip, 'IssueSlip should return 2 checkouts for today');
AddReturn( $barcode1, $branchcode );
AddReturn( $barcode2, $branchcode );
# Test 2: 1 Overdue
$date_due = $today_daily;
$issue_date = $today_daily->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
AddIssue( $borrower, $barcode1, $date_due, undef, $issue_date );
$date_due = $yesterday_daily;
$issue_date = $yesterday_daily;
AddIssue( $borrower, $barcode2, $date_due, undef, $issue_date );
$expected_slip = <<EOS;
Checked out today:
Title: $title1
Barcode: $barcode1
Date due: $today_daily_as_formatted
EOS
$slip = IssueSlip( $branchcode, $borrowernumber, 'quickslip' );
is( $slip->{content}, $expected_slip );
};
subtest 'Hourly loans' => sub {
plan tests => 2;
# Test 1: No overdue
my ( $date_due_in_time, $date_due_in_time_as_formatted, $date_due_in_late, $date_due_in_late_as_formatted, $issue_date, $slip, $expected_slip );
# Assuming today is not hour = 23 and minute = 59
$date_due_in_time = $today->clone->set(hour => ($today->hour < 23 ? $today->hour + 1 : 23), minute => 59);
$date_due_in_time_as_formatted = output_pref( $date_due_in_time );
$issue_date = $date_due_in_time->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
AddIssue( $borrower, $barcode1, $date_due_in_time, undef, $issue_date );
$issue_date = $yesterday->clone;
AddIssue( $borrower, $barcode2, $date_due_in_time, undef, $issue_date );
$expected_slip = <<EOS;
Checked out today:
Title: $title1
Barcode: $barcode1
Date due: $date_due_in_time_as_formatted
EOS
$slip = IssueSlip( $branchcode, $borrowernumber, 'quickslip' );
is( $slip->{content}, $expected_slip );
AddReturn( $barcode1, $branchcode );
AddReturn( $barcode2, $branchcode );
# Test 2: 1 Overdue
$date_due_in_time = $today->clone->set(hour => ($today->hour < 23 ? $today->hour + 1 : 23), minute => 59);
$date_due_in_time_as_formatted = output_pref( $date_due_in_time );
$issue_date = $date_due_in_time->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
AddIssue( $borrower, $barcode1, $date_due_in_time, undef, $issue_date );
$date_due_in_late = $today->clone->subtract( hours => 1 );
$date_due_in_late_as_formatted = output_pref( $date_due_in_late );
$issue_date = $yesterday->clone;
AddIssue( $borrower, $barcode2, $date_due_in_late, undef, $issue_date );
$expected_slip = <<EOS;
Checked out today:
Title: $title1
Barcode: $barcode1
Date due: $date_due_in_time_as_formatted
EOS
$slip = IssueSlip( $branchcode, $borrowernumber, 'quickslip' );
is( $slip->{content}, $expected_slip );
AddReturn( $barcode1, $branchcode );
AddReturn( $barcode2, $branchcode );
};
};
subtest 'bad calls' => sub {
plan tests => 1;
my $slip = IssueSlip();
is( $slip, undef, 'IssueSlip should return if no valid borrowernumber is passed' );
};
$schema->storage->txn_rollback;