X-Git-Url: http://koha-dev.rot13.org:8081/gitweb/?a=blobdiff_plain;f=t%2Fdb_dependent%2FKoha%2FPatrons.t;h=40d36ee49837c9706382ee481e0dd330cbd874f4;hb=0f907342c88e318ee6e774f8c085370681bc1af8;hp=9108f1ae1f64890aacb239cdff29e20e1b2946b8;hpb=ea3704203e88382024837c5caa2c264a153a3cf0;p=koha-ffzg.git diff --git a/t/db_dependent/Koha/Patrons.t b/t/db_dependent/Koha/Patrons.t index 9108f1ae1f..40d36ee498 100644 --- a/t/db_dependent/Koha/Patrons.t +++ b/t/db_dependent/Koha/Patrons.t @@ -19,14 +19,22 @@ use Modern::Perl; -use Test::More tests => 12; +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; @@ -48,6 +56,7 @@ my $new_patron_1 = Koha::Patron->new( 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( @@ -61,13 +70,19 @@ 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' ); my $retrieved_patron_1 = Koha::Patrons->find( $new_patron_1->borrowernumber ); is( $retrieved_patron_1->cardnumber, $new_patron_1->cardnumber, 'Find a patron by borrowernumber should return the correct patron' ); +subtest 'library' => sub { + plan tests => 2; + is( $retrieved_patron_1->library->branchcode, $library->{branchcode}, 'Koha::Patron->library should return the correct library' ); + is( ref($retrieved_patron_1->library), 'Koha::Library', 'Koha::Patron->library should return a Koha::Library object' ); +}; + subtest 'guarantees' => sub { plan tests => 8; my $guarantees = $new_patron_1->guarantees; @@ -137,11 +152,11 @@ subtest 'has_overdues' => sub { is( $retrieved_patron->has_overdues, 0, ); my $tomorrow = DateTime->today( time_zone => C4::Context->tz() )->add( days => 1 ); - my $issue = Koha::Issue->new({ borrowernumber => $new_patron_1->id, itemnumber => $item_1->{itemnumber}, date_due => $tomorrow, branchcode => $library->{branchcode} })->store(); + my $issue = Koha::Checkout->new({ borrowernumber => $new_patron_1->id, itemnumber => $item_1->{itemnumber}, date_due => $tomorrow, branchcode => $library->{branchcode} })->store(); is( $retrieved_patron->has_overdues, 0, ); $issue->delete(); my $yesterday = DateTime->today(time_zone => C4::Context->tz())->add( days => -1 ); - $issue = Koha::Issue->new({ borrowernumber => $new_patron_1->id, itemnumber => $item_1->{itemnumber}, date_due => $yesterday, branchcode => $library->{branchcode} })->store(); + $issue = Koha::Checkout->new({ borrowernumber => $new_patron_1->id, itemnumber => $item_1->{itemnumber}, date_due => $yesterday, branchcode => $library->{branchcode} })->store(); $retrieved_patron = Koha::Patrons->find( $new_patron_1->borrowernumber ); is( $retrieved_patron->has_overdues, 1, ); $issue->delete(); @@ -173,13 +188,11 @@ subtest 'update_password' => sub { }; 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; @@ -190,92 +203,147 @@ subtest 'is_expired' => sub { $patron->delete; }; +subtest 'is_going_to_expire' => sub { + 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'); + + t::lib::Mocks::mock_preference('NotifyBorrowerDeparture', 0); + $patron->dateexpiry( dt_from_string )->store->discard_changes; + is( $patron->is_going_to_expire, 0, 'Patron should not be considered going to expire if dateexpiry is today'); + + $patron->dateexpiry( dt_from_string )->store->discard_changes; + is( $patron->is_going_to_expire, 0, 'Patron should not be considered going to expire if dateexpiry is today and pref is 0'); + + t::lib::Mocks::mock_preference('NotifyBorrowerDeparture', 10); + $patron->dateexpiry( dt_from_string->add( days => 11 ) )->store->discard_changes; + is( $patron->is_going_to_expire, 0, 'Patron should not be considered going to expire if dateexpiry is 11 days ahead and pref is 10'); + + t::lib::Mocks::mock_preference('NotifyBorrowerDeparture', 0); + $patron->dateexpiry( dt_from_string->add( days => 10 ) )->store->discard_changes; + is( $patron->is_going_to_expire, 0, 'Patron should not be considered going to expire if dateexpiry is 10 days ahead and pref is 0'); + + t::lib::Mocks::mock_preference('NotifyBorrowerDeparture', 10); + $patron->dateexpiry( dt_from_string->add( days => 10 ) )->store->discard_changes; + is( $patron->is_going_to_expire, 0, 'Patron should not be considered going to expire if dateexpiry is 10 days ahead and pref is 10'); + $patron->delete; + + t::lib::Mocks::mock_preference('NotifyBorrowerDeparture', 10); + $patron->dateexpiry( dt_from_string->add( days => 20 ) )->store->discard_changes; + is( $patron->is_going_to_expire, 0, 'Patron should not be considered going to expire if dateexpiry is 20 days ahead and pref is 10'); + + t::lib::Mocks::mock_preference('NotifyBorrowerDeparture', 20); + $patron->dateexpiry( dt_from_string->add( days => 10 ) )->store->discard_changes; + is( $patron->is_going_to_expire, 1, 'Patron should be considered going to expire if dateexpiry is 10 days ahead and pref is 20'); + + $patron->delete; +}; + + 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 { - plan tests => 2; - my $patron = $builder->build( { source => 'Borrower' } ); + plan tests => 5; + my $originally_updated_on = '2016-01-01 12:12:12'; + my $patron = $builder->build( { source => 'Borrower',value => { updated_on => $originally_updated_on } } ); my $retrieved_patron = Koha::Patrons->find( $patron->{borrowernumber} ); is( ref( $retrieved_patron->move_to_deleted ), 'Koha::Schema::Result::Deletedborrower', 'Koha::Patron->move_to_deleted should return the Deleted patron' ) ; # FIXME This should be Koha::Deleted::Patron my $deleted_patron = $schema->resultset('Deletedborrower') ->search( { borrowernumber => $patron->{borrowernumber} }, { result_class => 'DBIx::Class::ResultClass::HashRefInflator' } ) ->next; + ok( $retrieved_patron->updated_on, 'updated_on should be set for borrowers table' ); + ok( $deleted_patron->{updated_on}, 'updated_on should be set for deleted_borrowers table' ); + isnt( $deleted_patron->{updated_on}, $retrieved_patron->updated_on, 'Koha::Patron->move_to_deleted should have correctly updated the updated_on column'); + $deleted_patron->{updated_on} = $originally_updated_on; #reset for simplicity in comparing all other fields is_deeply( $deleted_patron, $patron, 'Koha::Patron->move_to_deleted should have correctly moved the patron to the deleted table' ); $retrieved_patron->delete( $patron->{borrowernumber} ); # Cleanup }; @@ -312,14 +380,13 @@ subtest "delete" => 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', @@ -331,14 +398,15 @@ subtest 'add_enrolment_fee_if_needed' => sub { 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); @@ -346,25 +414,811 @@ subtest 'add_enrolment_fee_if_needed' => sub { $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 + old_checkouts' => sub { + plan tests => 12; + + 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, + itemlost => 0, + withdrawn => 0, + } + } + ); + my $item_2 = $builder->build( + { + source => 'Item', + value => { + homebranch => $library->{branchcode}, + holdingbranch => $library->{branchcode}, + biblionumber => $biblionumber_1, + itemlost => 0, + withdrawn => 0, + } + } + ); + my ($biblionumber_2) = AddBiblio( MARC::Record->new, '' ); + my $item_3 = $builder->build( + { + source => 'Item', + value => { + homebranch => $library->{branchcode}, + holdingbranch => $library->{branchcode}, + biblionumber => $biblionumber_2, + itemlost => 0, + withdrawn => 0, + } + } + ); + my $patron = $builder->build( + { + source => 'Borrower', + value => { branchcode => $library->{branchcode} } + } + ); + + $patron = Koha::Patrons->find( $patron->{borrowernumber} ); + 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 = Koha::Patrons->find( $patron->borrowernumber )->unblessed; + + 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} ); + $checkouts = $patron->checkouts; + is( $checkouts->count, 3, 'checkouts should return 3 issues for that patron' ); + is( ref($checkouts), 'Koha::Checkouts', 'checkouts should return a Koha::Checkouts object' ); + + my $overdues = $patron->get_overdues; + is( $overdues->count, 2, 'Patron should have 2 overdues'); + is( ref($overdues), 'Koha::Checkouts', 'Koha::Patron->get_overdues should return Koha::Checkouts' ); + 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 { + plan tests => 7; + + my $patron = $builder->build( { source => 'Borrower' } ); + $patron = Koha::Patrons->find( $patron->{borrowernumber} ); + + my $today = dt_from_string; + + $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, end_of_month => 'limit' ) ); + is( $patron->get_age, 12, 'Patron should be 12' ); + $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, end_of_month => 'limit' ) ); + is( $patron->get_age, 18, 'Patron should be 18' ); + $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, 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, end_of_month => 'limit' ) ); + is( $patron->get_age, 0, 'Patron is a newborn child' ); + + $patron->delete; +}; + +subtest 'account' => sub { + plan tests => 1; + + my $patron = $builder->build({source => 'Borrower'}); + + $patron = Koha::Patrons->find( $patron->{borrowernumber} ); + my $account = $patron->account; + is( ref($account), 'Koha::Account', 'account should return a Koha::Account object' ); + + $patron->delete; +}; + +subtest 'search_upcoming_membership_expires' => sub { + plan tests => 9; + + my $expiry_days = 15; + t::lib::Mocks::mock_preference( 'MembershipExpiryDaysNotice', $expiry_days ); + my $nb_of_days_before = 1; + my $nb_of_days_after = 2; + + my $builder = t::lib::TestBuilder->new(); + + my $library = $builder->build({ source => 'Branch' }); + + # before we add borrowers to this branch, add the expires we have now + # note that this pertains to the current mocked setting of the pref + # for this reason we add the new branchcode to most of the tests + my $nb_of_expires = Koha::Patrons->search_upcoming_membership_expires->count; + + my $patron_1 = $builder->build({ + source => 'Borrower', + value => { + branchcode => $library->{branchcode}, + dateexpiry => dt_from_string->add( days => $expiry_days ) + }, + }); + + my $patron_2 = $builder->build({ + source => 'Borrower', + value => { + branchcode => $library->{branchcode}, + dateexpiry => dt_from_string->add( days => $expiry_days - $nb_of_days_before ) + }, + }); + + my $patron_3 = $builder->build({ + source => 'Borrower', + value => { + branchcode => $library->{branchcode}, + dateexpiry => dt_from_string->add( days => $expiry_days + $nb_of_days_after ) + }, + }); + + # Test without extra parameters + my $upcoming_mem_expires = Koha::Patrons->search_upcoming_membership_expires(); + is( $upcoming_mem_expires->count, $nb_of_expires + 1, 'Get upcoming membership expires should return one new borrower.' ); + + # Test with branch + $upcoming_mem_expires = Koha::Patrons->search_upcoming_membership_expires({ 'me.branchcode' => $library->{branchcode} }); + is( $upcoming_mem_expires->count, 1, 'Test with branch parameter' ); + my $expired = $upcoming_mem_expires->next; + is( $expired->surname, $patron_1->{surname}, 'Get upcoming membership expires should return the correct patron.' ); + is( $expired->library->branchemail, $library->{branchemail}, 'Get upcoming membership expires should return the correct patron.' ); + is( $expired->branchcode, $patron_1->{branchcode}, 'Get upcoming membership expires should return the correct patron.' ); + + t::lib::Mocks::mock_preference( 'MembershipExpiryDaysNotice', 0 ); + $upcoming_mem_expires = Koha::Patrons->search_upcoming_membership_expires({ 'me.branchcode' => $library->{branchcode} }); + is( $upcoming_mem_expires->count, 0, 'Get upcoming membership expires with MembershipExpiryDaysNotice==0 should not return new records.' ); + + # Test MembershipExpiryDaysNotice == undef + t::lib::Mocks::mock_preference( 'MembershipExpiryDaysNotice', undef ); + $upcoming_mem_expires = Koha::Patrons->search_upcoming_membership_expires({ 'me.branchcode' => $library->{branchcode} }); + is( $upcoming_mem_expires->count, 0, 'Get upcoming membership expires without MembershipExpiryDaysNotice should not return new records.' ); + + # Test the before parameter + t::lib::Mocks::mock_preference( 'MembershipExpiryDaysNotice', 15 ); + $upcoming_mem_expires = Koha::Patrons->search_upcoming_membership_expires({ 'me.branchcode' => $library->{branchcode}, before => $nb_of_days_before }); + is( $upcoming_mem_expires->count, 2, 'Expect two results for before'); + # Test after parameter also + $upcoming_mem_expires = Koha::Patrons->search_upcoming_membership_expires({ 'me.branchcode' => $library->{branchcode}, before => $nb_of_days_before, after => $nb_of_days_after }); + is( $upcoming_mem_expires->count, 3, 'Expect three results when adding after' ); + Koha::Patrons->search({ borrowernumber => { in => [ $patron_1->{borrowernumber}, $patron_2->{borrowernumber}, $patron_3->{borrowernumber} ] } })->delete; +}; + +subtest 'holds and old_holds' => sub { + plan tests => 6; + + 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} } + } + ); + + $patron = Koha::Patrons->find( $patron->{borrowernumber} ); + my $holds = $patron->holds; + is( ref($holds), 'Koha::Holds', + 'Koha::Patron->holds should return a Koha::Holds objects' ); + is( $holds->count, 0, 'There should not be holds placed by this patron yet' ); + + C4::Reserves::AddReserve( $library->{branchcode}, + $patron->borrowernumber, $biblionumber_1 ); + # In the future + C4::Reserves::AddReserve( $library->{branchcode}, + $patron->borrowernumber, $biblionumber_2, undef, undef, dt_from_string->add( days => 2 ) ); + + $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, + '', '' + ); +}