use Modern::Perl;
-use Test::More tests => 20;
+use Test::More tests => 26;
use Test::Warn;
+use Time::Fake;
use DateTime;
+use JSON;
use C4::Biblio;
use C4::Circulation;
+
use C4::Members;
+use C4::Circulation;
use Koha::Holds;
use Koha::Patron;
use Koha::Patrons;
+use Koha::Patron::Categories;
use Koha::Database;
use Koha::DateUtils;
use Koha::Virtualshelves;
surname => 'surname for patron1',
firstname => 'firstname for patron1',
userid => 'a_nonexistent_userid_1',
+ flags => 1, # Is superlibrarian
}
)->store;
my $new_patron_2 = Koha::Patron->new(
)->store;
C4::Context->_new_userenv('xxx');
-C4::Context->set_userenv(0,0,0,'firstname','surname', $library->{branchcode}, 'Midway Public Library', '', '', '');
+set_logged_in_user( $new_patron_1 );
is( Koha::Patrons->search->count, $nb_of_patrons + 2, 'The 2 patrons should have been added' );
};
subtest 'is_expired' => sub {
- plan tests => 5;
+ plan tests => 4;
my $patron = $builder->build({ source => 'Borrower' });
$patron = Koha::Patrons->find( $patron->{borrowernumber} );
$patron->dateexpiry( undef )->store->discard_changes;
is( $patron->is_expired, 0, 'Patron should not be considered expired if dateexpiry is not set');
- $patron->dateexpiry( '0000-00-00' )->store->discard_changes;
- is( $patron->is_expired, 0, 'Patron should not be considered expired if dateexpiry is not 0000-00-00');
$patron->dateexpiry( dt_from_string )->store->discard_changes;
is( $patron->is_expired, 0, 'Patron should not be considered expired if dateexpiry is today');
$patron->dateexpiry( dt_from_string->add( days => 1 ) )->store->discard_changes;
};
subtest 'is_going_to_expire' => sub {
- plan tests => 9;
+ plan tests => 8;
my $patron = $builder->build({ source => 'Borrower' });
$patron = Koha::Patrons->find( $patron->{borrowernumber} );
$patron->dateexpiry( undef )->store->discard_changes;
is( $patron->is_going_to_expire, 0, 'Patron should not be considered going to expire if dateexpiry is not set');
- $patron->dateexpiry( '0000-00-00' )->store->discard_changes;
- is( $patron->is_going_to_expire, 0, 'Patron should not be considered going to expire if dateexpiry is not 0000-00-00');
t::lib::Mocks::mock_preference('NotifyBorrowerDeparture', 0);
$patron->dateexpiry( dt_from_string )->store->discard_changes;
subtest 'renew_account' => sub {
- plan tests => 10;
- my $a_month_ago = dt_from_string->add( months => -1 )->truncate( to => 'day' );
- my $a_year_later = dt_from_string->add( months => 12 )->truncate( to => 'day' );
- my $a_year_later_minus_a_month = dt_from_string->add( months => 11 )->truncate( to => 'day' );
- my $a_month_later = dt_from_string->add( months => 1 )->truncate( to => 'day' );
- my $a_year_later_plus_a_month = dt_from_string->add( months => 13 )->truncate( to => 'day' );
- my $patron_category = $builder->build(
- { source => 'Category',
- value => {
- enrolmentperiod => 12,
- enrolmentperioddate => undef,
+ plan tests => 36;
+
+ for my $date ( '2016-03-31', '2016-11-30', dt_from_string() ) {
+ my $dt = dt_from_string( $date, 'iso' );
+ Time::Fake->offset( $dt->epoch );
+ my $a_month_ago = $dt->clone->subtract( months => 1, end_of_month => 'limit' )->truncate( to => 'day' );
+ my $a_year_later = $dt->clone->add( months => 12, end_of_month => 'limit' )->truncate( to => 'day' );
+ my $a_year_later_minus_a_month = $dt->clone->add( months => 11, end_of_month => 'limit' )->truncate( to => 'day' );
+ my $a_month_later = $dt->clone->add( months => 1 , end_of_month => 'limit' )->truncate( to => 'day' );
+ my $a_year_later_plus_a_month = $dt->clone->add( months => 13, end_of_month => 'limit' )->truncate( to => 'day' );
+ my $patron_category = $builder->build(
+ { source => 'Category',
+ value => {
+ enrolmentperiod => 12,
+ enrolmentperioddate => undef,
+ }
}
- }
- );
- my $patron = $builder->build(
- { source => 'Borrower',
- value => {
- dateexpiry => $a_month_ago,
- categorycode => $patron_category->{categorycode},
+ );
+ my $patron = $builder->build(
+ { source => 'Borrower',
+ value => {
+ dateexpiry => $a_month_ago,
+ categorycode => $patron_category->{categorycode},
+ date_renewed => undef, # Force builder to not populate the column for new patron
+ }
}
- }
- );
- my $patron_2 = $builder->build(
- { source => 'Borrower',
- value => {
- dateexpiry => $a_month_ago,
- categorycode => $patron_category->{categorycode},
+ );
+ my $patron_2 = $builder->build(
+ { source => 'Borrower',
+ value => {
+ dateexpiry => $a_month_ago,
+ categorycode => $patron_category->{categorycode},
+ }
}
- }
- );
- my $patron_3 = $builder->build(
- { source => 'Borrower',
- value => {
- dateexpiry => $a_month_later,
- categorycode => $patron_category->{categorycode},
- }
- }
- );
- my $retrieved_patron = Koha::Patrons->find( $patron->{borrowernumber} );
- my $retrieved_patron_2 = Koha::Patrons->find( $patron_2->{borrowernumber} );
- my $retrieved_patron_3 = Koha::Patrons->find( $patron_3->{borrowernumber} );
-
- t::lib::Mocks::mock_preference( 'BorrowerRenewalPeriodBase', 'dateexpiry' );
- t::lib::Mocks::mock_preference( 'BorrowersLog', 1 );
- my $expiry_date = $retrieved_patron->renew_account;
- is( $expiry_date, $a_year_later_minus_a_month, );
- my $retrieved_expiry_date = Koha::Patrons->find( $patron->{borrowernumber} )->dateexpiry;
- is( dt_from_string($retrieved_expiry_date), $a_year_later_minus_a_month );
- my $number_of_logs = $schema->resultset('ActionLog')->search( { module => 'MEMBERS', action => 'RENEW', object => $retrieved_patron->borrowernumber } )->count;
- is( $number_of_logs, 1, 'With BorrowerLogs, Koha::Patron->renew_account should have logged' );
-
- t::lib::Mocks::mock_preference( 'BorrowerRenewalPeriodBase', 'now' );
- t::lib::Mocks::mock_preference( 'BorrowersLog', 0 );
- $expiry_date = $retrieved_patron->renew_account;
- is( $expiry_date, $a_year_later, );
- $retrieved_expiry_date = Koha::Patrons->find( $patron->{borrowernumber} )->dateexpiry;
- is( dt_from_string($retrieved_expiry_date), $a_year_later );
- $number_of_logs = $schema->resultset('ActionLog')->search( { module => 'MEMBERS', action => 'RENEW', object => $retrieved_patron->borrowernumber } )->count;
- is( $number_of_logs, 1, 'Without BorrowerLogs, Koha::Patron->renew_account should not have logged' );
-
- t::lib::Mocks::mock_preference( 'BorrowerRenewalPeriodBase', 'combination' );
- $expiry_date = $retrieved_patron_2->renew_account;
- is( $expiry_date, $a_year_later );
- $retrieved_expiry_date = Koha::Patrons->find( $patron_2->{borrowernumber} )->dateexpiry;
- is( dt_from_string($retrieved_expiry_date), $a_year_later );
-
- $expiry_date = $retrieved_patron_3->renew_account;
- is( $expiry_date, $a_year_later_plus_a_month );
- $retrieved_expiry_date = Koha::Patrons->find( $patron_3->{borrowernumber} )->dateexpiry;
- is( dt_from_string($retrieved_expiry_date), $a_year_later_plus_a_month );
-
- $retrieved_patron->delete;
- $retrieved_patron_2->delete;
- $retrieved_patron_3->delete;
+ );
+ my $patron_3 = $builder->build(
+ { source => 'Borrower',
+ value => {
+ dateexpiry => $a_month_later,
+ categorycode => $patron_category->{categorycode},
+ }
+ }
+ );
+ my $retrieved_patron = Koha::Patrons->find( $patron->{borrowernumber} );
+ my $retrieved_patron_2 = Koha::Patrons->find( $patron_2->{borrowernumber} );
+ my $retrieved_patron_3 = Koha::Patrons->find( $patron_3->{borrowernumber} );
+
+ is( $retrieved_patron->date_renewed, undef, "Date renewed is not set for patrons that have never been renewed" );
+
+ t::lib::Mocks::mock_preference( 'BorrowerRenewalPeriodBase', 'dateexpiry' );
+ t::lib::Mocks::mock_preference( 'BorrowersLog', 1 );
+ my $expiry_date = $retrieved_patron->renew_account;
+ is( $expiry_date, $a_year_later_minus_a_month, "$a_month_ago + 12 months must be $a_year_later_minus_a_month" );
+ my $retrieved_expiry_date = Koha::Patrons->find( $patron->{borrowernumber} )->dateexpiry;
+ is( dt_from_string($retrieved_expiry_date), $a_year_later_minus_a_month, "$a_month_ago + 12 months must be $a_year_later_minus_a_month" );
+ my $number_of_logs = $schema->resultset('ActionLog')->search( { module => 'MEMBERS', action => 'RENEW', object => $retrieved_patron->borrowernumber } )->count;
+ is( $number_of_logs, 1, 'With BorrowerLogs, Koha::Patron->renew_account should have logged' );
+
+ t::lib::Mocks::mock_preference( 'BorrowerRenewalPeriodBase', 'now' );
+ t::lib::Mocks::mock_preference( 'BorrowersLog', 0 );
+ $expiry_date = $retrieved_patron->renew_account;
+ is( $expiry_date, $a_year_later, "today + 12 months must be $a_year_later" );
+ $retrieved_patron = Koha::Patrons->find( $patron->{borrowernumber} );
+ is( $retrieved_patron->date_renewed, output_pref({ dt => $dt, dateformat => 'iso', dateonly => 1 }), "Date renewed is set when calling renew_account" );
+ $retrieved_expiry_date = $retrieved_patron->dateexpiry;
+ is( dt_from_string($retrieved_expiry_date), $a_year_later, "today + 12 months must be $a_year_later" );
+ $number_of_logs = $schema->resultset('ActionLog')->search( { module => 'MEMBERS', action => 'RENEW', object => $retrieved_patron->borrowernumber } )->count;
+ is( $number_of_logs, 1, 'Without BorrowerLogs, Koha::Patron->renew_account should not have logged' );
+
+ t::lib::Mocks::mock_preference( 'BorrowerRenewalPeriodBase', 'combination' );
+ $expiry_date = $retrieved_patron_2->renew_account;
+ is( $expiry_date, $a_year_later, "today + 12 months must be $a_year_later" );
+ $retrieved_expiry_date = Koha::Patrons->find( $patron_2->{borrowernumber} )->dateexpiry;
+ is( dt_from_string($retrieved_expiry_date), $a_year_later, "today + 12 months must be $a_year_later" );
+
+ $expiry_date = $retrieved_patron_3->renew_account;
+ is( $expiry_date, $a_year_later_plus_a_month, "$a_month_later + 12 months must be $a_year_later_plus_a_month" );
+ $retrieved_expiry_date = Koha::Patrons->find( $patron_3->{borrowernumber} )->dateexpiry;
+ is( dt_from_string($retrieved_expiry_date), $a_year_later_plus_a_month, "$a_month_later + 12 months must be $a_year_later_plus_a_month" );
+
+ $retrieved_patron->delete;
+ $retrieved_patron_2->delete;
+ $retrieved_patron_3->delete;
+ }
+ Time::Fake->reset;
};
subtest "move_to_deleted" => sub {
subtest 'add_enrolment_fee_if_needed' => sub {
plan tests => 4;
- my $enrolmentfee_K = 5;
- my $enrolmentfee_J = 10;
- my $enrolmentfee_YA = 20;
-
- my $dbh = C4::Context->dbh;
- $dbh->do(q|UPDATE categories set enrolmentfee=? where categorycode=?|, undef, $enrolmentfee_K, 'K');
- $dbh->do(q|UPDATE categories set enrolmentfee=? where categorycode=?|, undef, $enrolmentfee_J, 'J');
- $dbh->do(q|UPDATE categories set enrolmentfee=? where categorycode=?|, undef, $enrolmentfee_YA, 'YA');
+ my $enrolmentfees = { K => 5, J => 10, YA => 20 };
+ foreach( keys %{$enrolmentfees} ) {
+ ( Koha::Patron::Categories->find( $_ ) // $builder->build_object({ class => 'Koha::Patron::Categories', value => { categorycode => $_ } }) )->enrolmentfee( $enrolmentfees->{$_} )->store;
+ }
+ my $enrolmentfee_K = $enrolmentfees->{K};
+ my $enrolmentfee_J = $enrolmentfees->{J};
+ my $enrolmentfee_YA = $enrolmentfees->{YA};
my %borrower_data = (
firstname => 'my firstname',
my $borrowernumber = C4::Members::AddMember(%borrower_data);
$borrower_data{borrowernumber} = $borrowernumber;
- my ($total) = C4::Members::GetMemberAccountRecords($borrowernumber);
- is( $total, $enrolmentfee_K, "New kid pay $enrolmentfee_K" );
+ my $patron = Koha::Patrons->find( $borrowernumber );
+ my $total = $patron->account->balance;
+ is( int($total), int($enrolmentfee_K), "New kid pay $enrolmentfee_K" );
t::lib::Mocks::mock_preference( 'FeeOnChangePatronCategory', 0 );
$borrower_data{categorycode} = 'J';
C4::Members::ModMember(%borrower_data);
- ($total) = C4::Members::GetMemberAccountRecords($borrowernumber);
- is( $total, $enrolmentfee_K, "Kid growing and become a juvenile, but shouldn't pay for the upgrade " );
+ $total = $patron->account->balance;
+ is( int($total), int($enrolmentfee_K), "Kid growing and become a juvenile, but shouldn't pay for the upgrade " );
$borrower_data{categorycode} = 'K';
C4::Members::ModMember(%borrower_data);
$borrower_data{categorycode} = 'J';
C4::Members::ModMember(%borrower_data);
- ($total) = C4::Members::GetMemberAccountRecords($borrowernumber);
- is( $total, $enrolmentfee_K + $enrolmentfee_J, "Kid growing and become a juvenile, he should pay " . ( $enrolmentfee_K + $enrolmentfee_J ) );
+ $total = $patron->account->balance;
+ is( int($total), int($enrolmentfee_K + $enrolmentfee_J), "Kid growing and become a juvenile, they should pay " . ( $enrolmentfee_K + $enrolmentfee_J ) );
# Check with calling directly Koha::Patron->get_enrolment_fee_if_needed
- my $patron = Koha::Patrons->find($borrowernumber);
$patron->categorycode('YA')->store;
my $fee = $patron->add_enrolment_fee_if_needed;
- ($total) = C4::Members::GetMemberAccountRecords($borrowernumber);
- is( $total,
- $enrolmentfee_K + $enrolmentfee_J + $enrolmentfee_YA,
- "Juvenile growing and become an young adult, he should pay " . ( $enrolmentfee_K + $enrolmentfee_J + $enrolmentfee_YA )
+ $total = $patron->account->balance;
+ is( int($total),
+ int($enrolmentfee_K + $enrolmentfee_J + $enrolmentfee_YA),
+ "Juvenile growing and become an young adult, they should pay " . ( $enrolmentfee_K + $enrolmentfee_J + $enrolmentfee_YA )
);
$patron->delete;
};
-subtest 'checkouts + get_overdues' => sub {
- plan tests => 8;
+subtest 'checkouts + get_overdues + old_checkouts' => sub {
+ plan tests => 12;
my $library = $builder->build( { source => 'Branch' } );
my ($biblionumber_1) = AddBiblio( MARC::Record->new, '' );
value => {
homebranch => $library->{branchcode},
holdingbranch => $library->{branchcode},
- biblionumber => $biblionumber_1
+ biblionumber => $biblionumber_1,
+ itemlost => 0,
+ withdrawn => 0,
}
}
);
value => {
homebranch => $library->{branchcode},
holdingbranch => $library->{branchcode},
- biblionumber => $biblionumber_1
+ biblionumber => $biblionumber_1,
+ itemlost => 0,
+ withdrawn => 0,
}
}
);
value => {
homebranch => $library->{branchcode},
holdingbranch => $library->{branchcode},
- biblionumber => $biblionumber_2
+ biblionumber => $biblionumber_2,
+ itemlost => 0,
+ withdrawn => 0,
}
}
);
my $checkouts = $patron->checkouts;
is( $checkouts->count, 0, 'checkouts should not return any issues for that patron' );
is( ref($checkouts), 'Koha::Checkouts', 'checkouts should return a Koha::Checkouts object' );
+ my $old_checkouts = $patron->old_checkouts;
+ is( $old_checkouts->count, 0, 'old_checkouts should not return any issues for that patron' );
+ is( ref($old_checkouts), 'Koha::Old::Checkouts', 'old_checkouts should return a Koha::Old::Checkouts object' );
# Not sure how this is useful, but AddIssue pass this variable to different other subroutines
- $patron = GetMember( borrowernumber => $patron->borrowernumber );
+ $patron = Koha::Patrons->find( $patron->borrowernumber )->unblessed;
my $module = new Test::MockModule('C4::Context');
$module->mock( 'userenv', sub { { branch => $library->{branchcode} } } );
is( $overdues->next->itemnumber, $item_1->{itemnumber}, 'The issue should be returned in the same order as they have been done, first is correct' );
is( $overdues->next->itemnumber, $item_2->{itemnumber}, 'The issue should be returned in the same order as they have been done, second is correct' );
+
+ C4::Circulation::AddReturn( $item_1->{barcode} );
+ C4::Circulation::AddReturn( $item_2->{barcode} );
+ $old_checkouts = $patron->old_checkouts;
+ is( $old_checkouts->count, 2, 'old_checkouts should return 2 old checkouts that patron' );
+ is( ref($old_checkouts), 'Koha::Old::Checkouts', 'old_checkouts should return a Koha::Old::Checkouts object' );
+
# Clean stuffs
Koha::Checkouts->search( { borrowernumber => $patron->borrowernumber } )->delete;
$patron->delete;
+ $module->unmock('userenv');
};
subtest 'get_age' => sub {
$patron->dateofbirth( undef );
is( $patron->get_age, undef, 'get_age should return undef if no dateofbirth is defined' );
- $patron->dateofbirth( $today->clone->add( years => -12, months => -6, days => -1 ) );
+ $patron->dateofbirth( $today->clone->add( years => -12, months => -6, days => -1, end_of_month => 'limit' ) );
is( $patron->get_age, 12, 'Patron should be 12' );
- $patron->dateofbirth( $today->clone->add( years => -18, months => 0, days => 1 ) );
+ $patron->dateofbirth( $today->clone->add( years => -18, months => 0, days => 1, end_of_month => 'limit' ) );
is( $patron->get_age, 17, 'Patron should be 17, happy birthday tomorrow!' );
- $patron->dateofbirth( $today->clone->add( years => -18, months => 0, days => 0 ) );
+ $patron->dateofbirth( $today->clone->add( years => -18, months => 0, days => 0, end_of_month => 'limit' ) );
is( $patron->get_age, 18, 'Patron should be 18' );
- $patron->dateofbirth( $today->clone->add( years => -18, months => -12, days => -31 ) );
+ $patron->dateofbirth( $today->clone->add( years => -18, months => -12, days => -31, end_of_month => 'limit' ) );
is( $patron->get_age, 19, 'Patron should be 19' );
- $patron->dateofbirth( $today->clone->add( years => -18, months => -12, days => -30 ) );
+ $patron->dateofbirth( $today->clone->add( years => -18, months => -12, days => -30, end_of_month => 'limit' ) );
is( $patron->get_age, 19, 'Patron should be 19 again' );
- $patron->dateofbirth( $today->clone->add( years => 0, months => -1, days => -1 ) );
+ $patron->dateofbirth( $today->clone->add( years => 0, months => -1, days => -1, end_of_month => 'limit' ) );
is( $patron->get_age, 0, 'Patron is a newborn child' );
$patron->delete;
Koha::Patrons->search({ borrowernumber => { in => [ $patron_1->{borrowernumber}, $patron_2->{borrowernumber}, $patron_3->{borrowernumber} ] } })->delete;
};
-subtest 'holds' => sub {
- plan tests => 3;
+subtest 'holds and old_holds' => sub {
+ plan tests => 6;
my $library = $builder->build( { source => 'Branch' } );
my ($biblionumber_1) = AddBiblio( MARC::Record->new, '' );
$holds = $patron->holds;
is( $holds->count, 2, 'There should be 2 holds placed by this patron' );
+ my $old_holds = $patron->old_holds;
+ is( ref($old_holds), 'Koha::Old::Holds',
+ 'Koha::Patron->old_holds should return a Koha::Old::Holds objects' );
+ is( $old_holds->count, 0, 'There should not be any old holds yet');
+
+ my $hold = $holds->next;
+ $hold->cancel;
+
+ $old_holds = $patron->old_holds;
+ is( $old_holds->count, 1, 'There should be 1 old (cancelled) hold');
+
+ $old_holds->delete;
$holds->delete;
$patron->delete;
};
+subtest 'notice_email_address' => sub {
+ plan tests => 2;
+
+ my $patron = $builder->build_object({ class => 'Koha::Patrons' });
+
+ t::lib::Mocks::mock_preference( 'AutoEmailPrimaryAddress', 'OFF' );
+ is ($patron->notice_email_address, $patron->email, "Koha::Patron->notice_email_address returns correct value when AutoEmailPrimaryAddress is off");
+
+ t::lib::Mocks::mock_preference( 'AutoEmailPrimaryAddress', 'emailpro' );
+ is ($patron->notice_email_address, $patron->emailpro, "Koha::Patron->notice_email_address returns correct value when AutoEmailPrimaryAddress is emailpro");
+
+ $patron->delete;
+};
+
+subtest 'search_patrons_to_anonymise & anonymise_issue_history' => sub {
+ plan tests => 4;
+
+ # TODO create a subroutine in t::lib::Mocks
+ my $branch = $builder->build({ source => 'Branch' });
+ my $userenv_patron = $builder->build({
+ source => 'Borrower',
+ value => { branchcode => $branch->{branchcode} },
+ });
+ C4::Context->_new_userenv('DUMMY SESSION');
+ C4::Context->set_userenv(
+ $userenv_patron->{borrowernumber},
+ $userenv_patron->{userid},
+ 'usercnum', 'First name', 'Surname',
+ $branch->{branchcode},
+ $branch->{branchname},
+ 0,
+ );
+ my $anonymous = $builder->build( { source => 'Borrower', }, );
+
+ t::lib::Mocks::mock_preference( 'AnonymousPatron', $anonymous->{borrowernumber} );
+
+ subtest 'patron privacy is 1 (default)' => sub {
+ plan tests => 8;
+
+ t::lib::Mocks::mock_preference('IndependentBranches', 0);
+ my $patron = $builder->build(
+ { source => 'Borrower',
+ value => { privacy => 1, }
+ }
+ );
+ my $item_1 = $builder->build(
+ { source => 'Item',
+ value => {
+ itemlost => 0,
+ withdrawn => 0,
+ },
+ }
+ );
+ my $issue_1 = $builder->build(
+ { source => 'Issue',
+ value => {
+ borrowernumber => $patron->{borrowernumber},
+ itemnumber => $item_1->{itemnumber},
+ },
+ }
+ );
+ my $item_2 = $builder->build(
+ { source => 'Item',
+ value => {
+ itemlost => 0,
+ withdrawn => 0,
+ },
+ }
+ );
+ my $issue_2 = $builder->build(
+ { source => 'Issue',
+ value => {
+ borrowernumber => $patron->{borrowernumber},
+ itemnumber => $item_2->{itemnumber},
+ },
+ }
+ );
+
+ my ( $returned_1, undef, undef ) = C4::Circulation::AddReturn( $item_1->{barcode}, undef, undef, undef, '2010-10-10' );
+ my ( $returned_2, undef, undef ) = C4::Circulation::AddReturn( $item_2->{barcode}, undef, undef, undef, '2011-11-11' );
+ is( $returned_1 && $returned_2, 1, 'The items should have been returned' );
+
+ my $patrons_to_anonymise = Koha::Patrons->search_patrons_to_anonymise( { before => '2010-10-11' } )->search( { 'me.borrowernumber' => $patron->{borrowernumber} } );
+ is( ref($patrons_to_anonymise), 'Koha::Patrons', 'search_patrons_to_anonymise should return Koha::Patrons' );
+
+ my $rows_affected = Koha::Patrons->search_patrons_to_anonymise( { before => '2011-11-12' } )->anonymise_issue_history( { before => '2010-10-11' } );
+ ok( $rows_affected > 0, 'AnonymiseIssueHistory should affect at least 1 row' );
+
+ my $dbh = C4::Context->dbh;
+ my $sth = $dbh->prepare(q|SELECT borrowernumber FROM old_issues where itemnumber = ?|);
+ $sth->execute($item_1->{itemnumber});
+ my ($borrowernumber_used_to_anonymised) = $sth->fetchrow_array;
+ is( $borrowernumber_used_to_anonymised, $anonymous->{borrowernumber}, 'With privacy=1, the issue should have been anonymised' );
+ $sth->execute($item_2->{itemnumber});
+ ($borrowernumber_used_to_anonymised) = $sth->fetchrow_array;
+ is( $borrowernumber_used_to_anonymised, $patron->{borrowernumber}, 'The issue should not have been anonymised, the returned date is later' );
+
+ $rows_affected = Koha::Patrons->search_patrons_to_anonymise( { before => '2011-11-12' } )->anonymise_issue_history;
+ $sth->execute($item_2->{itemnumber});
+ ($borrowernumber_used_to_anonymised) = $sth->fetchrow_array;
+ is( $borrowernumber_used_to_anonymised, $anonymous->{borrowernumber}, 'The issue should have been anonymised, the returned date is before' );
+
+ my $sth_reset = $dbh->prepare(q|UPDATE old_issues SET borrowernumber = ? WHERE itemnumber = ?|);
+ $sth_reset->execute( $patron->{borrowernumber}, $item_1->{itemnumber} );
+ $sth_reset->execute( $patron->{borrowernumber}, $item_2->{itemnumber} );
+ $rows_affected = Koha::Patrons->search_patrons_to_anonymise->anonymise_issue_history;
+ $sth->execute($item_1->{itemnumber});
+ ($borrowernumber_used_to_anonymised) = $sth->fetchrow_array;
+ is( $borrowernumber_used_to_anonymised, $anonymous->{borrowernumber}, 'The issue 1 should have been anonymised, before parameter was not passed' );
+ $sth->execute($item_2->{itemnumber});
+ ($borrowernumber_used_to_anonymised) = $sth->fetchrow_array;
+ is( $borrowernumber_used_to_anonymised, $anonymous->{borrowernumber}, 'The issue 2 should have been anonymised, before parameter was not passed' );
+
+ Koha::Patrons->find( $patron->{borrowernumber})->delete;
+ };
+
+ subtest 'patron privacy is 0 (forever)' => sub {
+ plan tests => 3;
+
+ t::lib::Mocks::mock_preference('IndependentBranches', 0);
+ my $patron = $builder->build(
+ { source => 'Borrower',
+ value => { privacy => 0, }
+ }
+ );
+ my $item = $builder->build(
+ { source => 'Item',
+ value => {
+ itemlost => 0,
+ withdrawn => 0,
+ },
+ }
+ );
+ my $issue = $builder->build(
+ { source => 'Issue',
+ value => {
+ borrowernumber => $patron->{borrowernumber},
+ itemnumber => $item->{itemnumber},
+ },
+ }
+ );
+
+ my ( $returned, undef, undef ) = C4::Circulation::AddReturn( $item->{barcode}, undef, undef, undef, '2010-10-10' );
+ is( $returned, 1, 'The item should have been returned' );
+ my $rows_affected = Koha::Patrons->search_patrons_to_anonymise( { before => '2010-10-11' } )->anonymise_issue_history( { before => '2010-10-11' } );
+ ok( $rows_affected > 0, 'AnonymiseIssueHistory should not return any error if success' );
+
+ my $dbh = C4::Context->dbh;
+ my ($borrowernumber_used_to_anonymised) = $dbh->selectrow_array(q|
+ SELECT borrowernumber FROM old_issues where itemnumber = ?
+ |, undef, $item->{itemnumber});
+ is( $borrowernumber_used_to_anonymised, $patron->{borrowernumber}, 'With privacy=0, the issue should not be anonymised' );
+ Koha::Patrons->find( $patron->{borrowernumber})->delete;
+ };
+
+ t::lib::Mocks::mock_preference( 'AnonymousPatron', '' );
+
+ subtest 'AnonymousPatron is not defined' => sub {
+ plan tests => 3;
+
+ t::lib::Mocks::mock_preference('IndependentBranches', 0);
+ my $patron = $builder->build(
+ { source => 'Borrower',
+ value => { privacy => 1, }
+ }
+ );
+ my $item = $builder->build(
+ { source => 'Item',
+ value => {
+ itemlost => 0,
+ withdrawn => 0,
+ },
+ }
+ );
+ my $issue = $builder->build(
+ { source => 'Issue',
+ value => {
+ borrowernumber => $patron->{borrowernumber},
+ itemnumber => $item->{itemnumber},
+ },
+ }
+ );
+
+ my ( $returned, undef, undef ) = C4::Circulation::AddReturn( $item->{barcode}, undef, undef, undef, '2010-10-10' );
+ is( $returned, 1, 'The item should have been returned' );
+ my $rows_affected = Koha::Patrons->search_patrons_to_anonymise( { before => '2010-10-11' } )->anonymise_issue_history( { before => '2010-10-11' } );
+ ok( $rows_affected > 0, 'AnonymiseIssueHistory should affect at least 1 row' );
+
+ my $dbh = C4::Context->dbh;
+ my ($borrowernumber_used_to_anonymised) = $dbh->selectrow_array(q|
+ SELECT borrowernumber FROM old_issues where itemnumber = ?
+ |, undef, $item->{itemnumber});
+ is( $borrowernumber_used_to_anonymised, undef, 'With AnonymousPatron is not defined, the issue should have been anonymised anyway' );
+ Koha::Patrons->find( $patron->{borrowernumber})->delete;
+ };
+
+ subtest 'Logged in librarian is not superlibrarian & IndependentBranches' => sub {
+ plan tests => 1;
+ t::lib::Mocks::mock_preference( 'IndependentBranches', 1 );
+ my $patron = $builder->build(
+ { source => 'Borrower',
+ value => { privacy => 1 } # Another branchcode than the logged in librarian
+ }
+ );
+ my $item = $builder->build(
+ { source => 'Item',
+ value => {
+ itemlost => 0,
+ withdrawn => 0,
+ },
+ }
+ );
+ my $issue = $builder->build(
+ { source => 'Issue',
+ value => {
+ borrowernumber => $patron->{borrowernumber},
+ itemnumber => $item->{itemnumber},
+ },
+ }
+ );
+
+ my ( $returned, undef, undef ) = C4::Circulation::AddReturn( $item->{barcode}, undef, undef, undef, '2010-10-10' );
+ is( Koha::Patrons->search_patrons_to_anonymise( { before => '2010-10-11' } )->count, 0 );
+ Koha::Patrons->find( $patron->{borrowernumber})->delete;
+ };
+
+ Koha::Patrons->find( $anonymous->{borrowernumber})->delete;
+ Koha::Patrons->find( $userenv_patron->{borrowernumber})->delete;
+
+ # Reset IndependentBranches for further tests
+ t::lib::Mocks::mock_preference('IndependentBranches', 0);
+};
+
+subtest 'libraries_where_can_see_patrons + can_see_patron_infos + search_limited' => sub {
+ plan tests => 3;
+
+ # group1
+ # + library_11
+ # + library_12
+ # group2
+ # + library21
+ my $group_1 = Koha::Library::Group->new( { title => 'TEST Group 1', ft_hide_patron_info => 1 } )->store;
+ my $group_2 = Koha::Library::Group->new( { title => 'TEST Group 2', ft_hide_patron_info => 1 } )->store;
+ my $library_11 = $builder->build( { source => 'Branch' } );
+ my $library_12 = $builder->build( { source => 'Branch' } );
+ my $library_21 = $builder->build( { source => 'Branch' } );
+ $library_11 = Koha::Libraries->find( $library_11->{branchcode} );
+ $library_12 = Koha::Libraries->find( $library_12->{branchcode} );
+ $library_21 = Koha::Libraries->find( $library_21->{branchcode} );
+ Koha::Library::Group->new(
+ { branchcode => $library_11->branchcode, parent_id => $group_1->id } )->store;
+ Koha::Library::Group->new(
+ { branchcode => $library_12->branchcode, parent_id => $group_1->id } )->store;
+ Koha::Library::Group->new(
+ { branchcode => $library_21->branchcode, parent_id => $group_2->id } )->store;
+
+ my $sth = C4::Context->dbh->prepare(q|INSERT INTO user_permissions( borrowernumber, module_bit, code ) VALUES (?, 4, ?)|); # 4 for borrowers
+ # 2 patrons from library_11 (group1)
+ # patron_11_1 see patron's infos from outside its group
+ # Setting flags => undef to not be considered as superlibrarian
+ my $patron_11_1 = $builder->build({ source => 'Borrower', value => { branchcode => $library_11->branchcode, flags => undef, }});
+ $patron_11_1 = Koha::Patrons->find( $patron_11_1->{borrowernumber} );
+ $sth->execute( $patron_11_1->borrowernumber, 'edit_borrowers' );
+ $sth->execute( $patron_11_1->borrowernumber, 'view_borrower_infos_from_any_libraries' );
+ # patron_11_2 can only see patron's info from its group
+ my $patron_11_2 = $builder->build({ source => 'Borrower', value => { branchcode => $library_11->branchcode, flags => undef, }});
+ $patron_11_2 = Koha::Patrons->find( $patron_11_2->{borrowernumber} );
+ $sth->execute( $patron_11_2->borrowernumber, 'edit_borrowers' );
+ # 1 patron from library_12 (group1)
+ my $patron_12 = $builder->build({ source => 'Borrower', value => { branchcode => $library_12->branchcode, flags => undef, }});
+ $patron_12 = Koha::Patrons->find( $patron_12->{borrowernumber} );
+ # 1 patron from library_21 (group2) can only see patron's info from its group
+ my $patron_21 = $builder->build({ source => 'Borrower', value => { branchcode => $library_21->branchcode, flags => undef, }});
+ $patron_21 = Koha::Patrons->find( $patron_21->{borrowernumber} );
+ $sth->execute( $patron_21->borrowernumber, 'edit_borrowers' );
+
+ # Pfiou, we can start now!
+ subtest 'libraries_where_can_see_patrons' => sub {
+ plan tests => 3;
+
+ my @branchcodes;
+
+ set_logged_in_user( $patron_11_1 );
+ @branchcodes = $patron_11_1->libraries_where_can_see_patrons;
+ is_deeply( \@branchcodes, [], q|patron_11_1 has view_borrower_infos_from_any_libraries => No restriction| );
+
+ set_logged_in_user( $patron_11_2 );
+ @branchcodes = $patron_11_2->libraries_where_can_see_patrons;
+ is_deeply( \@branchcodes, [ sort ( $library_11->branchcode, $library_12->branchcode ) ], q|patron_11_2 has not view_borrower_infos_from_any_libraries => Can only see patron's from its group| );
+
+ set_logged_in_user( $patron_21 );
+ @branchcodes = $patron_21->libraries_where_can_see_patrons;
+ is_deeply( \@branchcodes, [$library_21->branchcode], q|patron_21 has not view_borrower_infos_from_any_libraries => Can only see patron's from its group| );
+ };
+ subtest 'can_see_patron_infos' => sub {
+ plan tests => 6;
+
+ set_logged_in_user( $patron_11_1 );
+ is( $patron_11_1->can_see_patron_infos( $patron_11_2 ), 1, q|patron_11_1 can see patron_11_2, from its library| );
+ is( $patron_11_1->can_see_patron_infos( $patron_12 ), 1, q|patron_11_1 can see patron_12, from its group| );
+ is( $patron_11_1->can_see_patron_infos( $patron_21 ), 1, q|patron_11_1 can see patron_11_2, from another group| );
+
+ set_logged_in_user( $patron_11_2 );
+ is( $patron_11_2->can_see_patron_infos( $patron_11_1 ), 1, q|patron_11_2 can see patron_11_1, from its library| );
+ is( $patron_11_2->can_see_patron_infos( $patron_12 ), 1, q|patron_11_2 can see patron_12, from its group| );
+ is( $patron_11_2->can_see_patron_infos( $patron_21 ), 0, q|patron_11_2 can NOT see patron_21, from another group| );
+ };
+ subtest 'search_limited' => sub {
+ plan tests => 6;
+
+ set_logged_in_user( $patron_11_1 );
+ my $total_number_of_patrons = $nb_of_patrons + 6; # 2 created before + 4 for these subtests
+ is( Koha::Patrons->search->count, $total_number_of_patrons, 'Non-limited search should return all patrons');
+ is( Koha::Patrons->search_limited->count, $total_number_of_patrons, 'patron_11_1 is allowed to see all patrons' );
+
+ set_logged_in_user( $patron_11_2 );
+ is( Koha::Patrons->search->count, $total_number_of_patrons, 'Non-limited search should return all patrons');
+ is( Koha::Patrons->search_limited->count, 3, 'patron_12_1 is not allowed to see patrons from other groups, only patron_11_1, patron_11_2 and patron_12' );
+
+ set_logged_in_user( $patron_21 );
+ is( Koha::Patrons->search->count, $total_number_of_patrons, 'Non-limited search should return all patrons');
+ is( Koha::Patrons->search_limited->count, 1, 'patron_21 is not allowed to see patrons from other groups, only himself' );
+ };
+ $patron_11_1->delete;
+ $patron_11_2->delete;
+ $patron_12->delete;
+ $patron_21->delete;
+};
+
+subtest 'account_locked' => sub {
+ plan tests => 8;
+ my $patron = $builder->build({ source => 'Borrower', value => { login_attempts => 0 } });
+ $patron = Koha::Patrons->find( $patron->{borrowernumber} );
+ for my $value ( undef, '', 0 ) {
+ t::lib::Mocks::mock_preference('FailedloginAttempts', $value);
+ is( $patron->account_locked, 0, 'Feature is disabled, patron account should not be considered locked' );
+ $patron->login_attempts(1)->store;
+ is( $patron->account_locked, 0, 'Feature is disabled, patron account should not be considered locked' );
+ }
+
+ t::lib::Mocks::mock_preference('FailedloginAttempts', 3);
+ $patron->login_attempts(2)->store;
+ is( $patron->account_locked, 0, 'Patron has 2 failed attempts, account should not be considered locked yet' );
+ $patron->login_attempts(3)->store;
+ is( $patron->account_locked, 1, 'Patron has 3 failed attempts, account should be considered locked yet' );
+
+ $patron->delete;
+};
+
+subtest 'is_child | is_adult' => sub {
+ plan tests => 8;
+ my $category = $builder->build_object(
+ {
+ class => 'Koha::Patron::Categories',
+ value => { category_type => 'A' }
+ }
+ );
+ my $patron_adult = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { categorycode => $category->categorycode }
+ }
+ );
+ $category = $builder->build_object(
+ {
+ class => 'Koha::Patron::Categories',
+ value => { category_type => 'I' }
+ }
+ );
+ my $patron_adult_i = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { categorycode => $category->categorycode }
+ }
+ );
+ $category = $builder->build_object(
+ {
+ class => 'Koha::Patron::Categories',
+ value => { category_type => 'C' }
+ }
+ );
+ my $patron_child = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { categorycode => $category->categorycode }
+ }
+ );
+ $category = $builder->build_object(
+ {
+ class => 'Koha::Patron::Categories',
+ value => { category_type => 'O' }
+ }
+ );
+ my $patron_other = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { categorycode => $category->categorycode }
+ }
+ );
+ is( $patron_adult->is_adult, 1, 'Patron from category A should be considered adult' );
+ is( $patron_adult_i->is_adult, 1, 'Patron from category I should be considered adult' );
+ is( $patron_child->is_adult, 0, 'Patron from category C should not be considered adult' );
+ is( $patron_other->is_adult, 0, 'Patron from category O should not be considered adult' );
+
+ is( $patron_adult->is_child, 0, 'Patron from category A should be considered child' );
+ is( $patron_adult_i->is_child, 0, 'Patron from category I should be considered child' );
+ is( $patron_child->is_child, 1, 'Patron from category C should not be considered child' );
+ is( $patron_other->is_child, 0, 'Patron from category O should not be considered child' );
+
+ # Clean up
+ $patron_adult->delete;
+ $patron_adult_i->delete;
+ $patron_child->delete;
+ $patron_other->delete;
+};
+
+subtest 'get_overdues' => sub {
+ plan tests => 7;
+
+ my $library = $builder->build( { source => 'Branch' } );
+ my ($biblionumber_1) = AddBiblio( MARC::Record->new, '' );
+ my $item_1 = $builder->build(
+ {
+ source => 'Item',
+ value => {
+ homebranch => $library->{branchcode},
+ holdingbranch => $library->{branchcode},
+ biblionumber => $biblionumber_1
+ }
+ }
+ );
+ my $item_2 = $builder->build(
+ {
+ source => 'Item',
+ value => {
+ homebranch => $library->{branchcode},
+ holdingbranch => $library->{branchcode},
+ biblionumber => $biblionumber_1
+ }
+ }
+ );
+ my ($biblionumber_2) = AddBiblio( MARC::Record->new, '' );
+ my $item_3 = $builder->build(
+ {
+ source => 'Item',
+ value => {
+ homebranch => $library->{branchcode},
+ holdingbranch => $library->{branchcode},
+ biblionumber => $biblionumber_2
+ }
+ }
+ );
+ my $patron = $builder->build(
+ {
+ source => 'Borrower',
+ value => { branchcode => $library->{branchcode} }
+ }
+ );
+
+ my $module = new Test::MockModule('C4::Context');
+ $module->mock( 'userenv', sub { { branch => $library->{branchcode} } } );
+
+ AddIssue( $patron, $item_1->{barcode}, DateTime->now->subtract( days => 1 ) );
+ AddIssue( $patron, $item_2->{barcode}, DateTime->now->subtract( days => 5 ) );
+ AddIssue( $patron, $item_3->{barcode} );
+
+ $patron = Koha::Patrons->find( $patron->{borrowernumber} );
+ my $overdues = $patron->get_overdues;
+ is( $overdues->count, 2, 'Patron should have 2 overdues');
+ is( $overdues->next->itemnumber, $item_1->{itemnumber}, 'The issue should be returned in the same order as they have been done, first is correct' );
+ is( $overdues->next->itemnumber, $item_2->{itemnumber}, 'The issue should be returned in the same order as they have been done, second is correct' );
+
+ my $o = $overdues->reset->next;
+ my $unblessed_overdue = $o->unblessed_all_relateds;
+ is( exists( $unblessed_overdue->{issuedate} ), 1, 'Fields from the issues table should be filled' );
+ is( exists( $unblessed_overdue->{itemcallnumber} ), 1, 'Fields from the items table should be filled' );
+ is( exists( $unblessed_overdue->{title} ), 1, 'Fields from the biblio table should be filled' );
+ is( exists( $unblessed_overdue->{itemtype} ), 1, 'Fields from the biblioitems table should be filled' );
+
+ # Clean stuffs
+ $patron->checkouts->delete;
+ $patron->delete;
+};
+
$retrieved_patron_1->delete;
is( Koha::Patrons->search->count, $nb_of_patrons + 1, 'Delete should have deleted the patron' );
+subtest 'Log cardnumber change' => sub {
+ plan tests => 3;
+
+ t::lib::Mocks::mock_preference( 'BorrowersLog', 1 );
+ my $patron = $builder->build( { source => 'Borrower' } );
+
+ my $cardnumber = $patron->{cardnumber};
+ $patron->{cardnumber} = 'TESTCARDNUMBER';
+ ModMember(%$patron);
+
+ my @logs = $schema->resultset('ActionLog')->search( { module => 'MEMBERS', action => 'MODIFY', object => $patron->{borrowernumber} } );
+ my $log_info = from_json( $logs[0]->info );
+ is( $log_info->{cardnumber_replaced}->{new_cardnumber}, 'TESTCARDNUMBER', 'Got correct new cardnumber' );
+ is( $log_info->{cardnumber_replaced}->{previous_cardnumber}, $cardnumber, 'Got correct old cardnumber' );
+ is( scalar @logs, 2, 'With BorrowerLogs, Change in cardnumber should be logged, as well as general alert of patron mod.' );
+};
+
+
$schema->storage->txn_rollback;
-1;
+# TODO Move to t::lib::Mocks and reuse it!
+sub set_logged_in_user {
+ my ($patron) = @_;
+ C4::Context->set_userenv(
+ $patron->borrowernumber, $patron->userid,
+ $patron->cardnumber, 'firstname',
+ 'surname', $patron->library->branchcode,
+ 'Midway Public Library', $patron->flags,
+ '', ''
+ );
+}